Repository: jDan735/wikipya (language: python)

Issue:
Add examples



Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
index 01bd5d0..78c1ea5 100644
--- a/.flake8
+++ b/.flake8
@@ -1,7 +1,8 @@
 [flake8]
-inline-quotes = "
 application-import-names = wikipya, types
 import-style-order = google
 
 strictness=short
 docstring_style=sphinx
+max-line-length = 100
+inline-quotes = "
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..0cc1976
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+recursive-include wikipya/methods *.py
+recursive-include wikipya/models *.py
diff --git a/README.md b/README.md
index 554f070..61510b2 100644
--- a/README.md
+++ b/README.md
@@ -8,14 +8,14 @@
 # Import wikipya
 from wikipya import Wikipya
 
-# Create Wikipya object with wikipedia methods
-wiki = Wikipya("en", lurk=False)
+# Create Wikipya object with Wikipedia methods
+wiki = Wikipya(lang="en").get_instance()
 
-# or use other mediawiki server (or other service, but this is no supported now)
+# or use other MediaEiki server (or other service, but this is'n fully supported now)
 
-wikipya = Wikipya(url="https://ipv6.lurkmo.re/api.php", prefix="")
+wikipya = Wikipya(url="https://ipv6.lurkmo.re/api.php", lurk=True, prefix="").get_instance()
 
-# for use lurkmore. simple and fast
+# for use Lurkmore (russian). simple and fast
 
 # Get a pages list from search
 search = await wiki.search("test")
@@ -36,12 +36,10 @@ page = await wiki.page("git")
 page = await wiki.page(800543)
 
 print(page.html)       # Get page html
-print(page.soup)       # Get parsed html (Beatiful Soup 4)
-print(page.parsed)     # Get html cleared of link, and other non-formating tags 
-print(page.fixed)      # [RU/UK] Свойство, в котором исправлены спорные слова (В/НА, Беларусь)
+print(page.parsed)     # Get html cleared of link, and other non-formating tags
 
-# Get page image
-image = await page.image()  # may not work in non-wikipedia services, check true prefix, or create issue
+# Get image
+image = await wiki.image(page.title)  # may not work in non-wikipedia services, check true prefix, or create issue
 
 print(image.source)    # Image url
 print(image.width)     # Image width
@@ -52,6 +50,7 @@ print(image.height)    # Image height
 - Full async
 - Support of other instances of MediaWiki
 - Support cleaning of HTML with TgHTML
+- Uses models by [pydantic](https://github.com/samuelcolvin/pydantic)
 
 ## 🚀 Install
 To install, run this code:
diff --git a/examples/get_full_page.py b/examples/get_full_page.py
new file mode 100644
index 0000000..ffa391b
--- /dev/null
+++ b/examples/get_full_page.py
@@ -0,0 +1,17 @@
+import asyncio
+from wikipya import Wikipya
+
+
+wiki = Wikipya("ru").get_instance()
+
+
+async def main():
+    page, image, url = await wiki.get_all("канобу")
+
+    print(page.parsed)
+    print(image)
+    print(url)
+
+
+if __name__ == "__main__":
+    asyncio.get_event_loop().run_until_complete(main())
diff --git a/examples/get_page_by_name.py b/examples/get_page_by_name.py
new file mode 100644
index 0000000..e1583cb
--- /dev/null
+++ b/examples/get_page_by_name.py
@@ -0,0 +1,16 @@
+import asyncio
+from wikipya import Wikipya
+
+
+wiki = Wikipya("ru").get_instance()
+
+
+async def main():
+    page = await wiki.page("путин")
+
+    print(page.title)
+    print(page.parsed[:200])
+
+
+if __name__ == "__main__":
+    asyncio.get_event_loop().run_until_complete(main())
diff --git a/examples/get_sections.py b/examples/get_sections.py
new file mode 100644
index 0000000..739e89d
--- /dev/null
+++ b/examples/get_sections.py
@@ -0,0 +1,16 @@
+import asyncio
+from wikipya import Wikipya
+
+
+wiki = Wikipya("ru").get_instance()
+
+
+async def main():
+    sections = await wiki.sections("канобу")
+
+    for section in sections.sections:
+        print(section.number, section.title)
+
+
+if __name__ == "__main__":
+    asyncio.get_event_loop().run_until_complete(main())
diff --git a/examples/search_pages.py b/examples/search_pages.py
new file mode 100644
index 0000000..b145baf
--- /dev/null
+++ b/examples/search_pages.py
@@ -0,0 +1,19 @@
+import asyncio
+from wikipya import Wikipya
+
+
+wiki = Wikipya("ru").get_instance()
+
+
+async def main():
+    results = await wiki.search("канобу", limit=5)
+
+    for result in results:
+        print(result.title)
+        print(result.snippet)
+        print(result.page_id)
+        print()
+
+
+if __name__ == "__main__":
+    asyncio.get_event_loop().run_until_complete(main())
diff --git a/setup.py b/setup.py
index 60b82a5..29c0a78 100644
--- a/setup.py
+++ b/setup.py
@@ -16,6 +16,7 @@
     url="https://github.com/jDan735/wikipya",
     license="MIT",
     packages=["wikipya"],
+    include_package_data=True,
     classifiers=[
         "Programming Language :: Python :: 3",
         "Programming Language :: Python :: 3.4",
@@ -29,7 +30,8 @@
     install_requires=[
         "beautifulsoup4",
         "tghtml",
-        "httpx"
+        "httpx",
+        "pydantic"
     ],
     test_suite="tests"
 )
diff --git a/wikipya/__init__.py b/wikipya/__init__.py
index 87141be..b8e5fd4 100644
--- a/wikipya/__init__.py
+++ b/wikipya/__init__.py
@@ -1,7 +1,10 @@
-__version__ = "3.0.0"
+import traceback
 
 try:
     from .aiowiki import Wikipya, NotFound
-except Exception as e:
-    print(e)
-    pass
+except ImportError:
+    print(traceback.format_exc())
+
+
+__version__ = "4.0.0b3.post2"
+__all__ = ("Wikipya", "NotFound")
diff --git a/wikipya/aiowiki.py b/wikipya/aiowiki.py
index f30a8a9..9c6684b 100644
--- a/wikipya/aiowiki.py
+++ b/wikipya/aiowiki.py
@@ -1,40 +1,32 @@
-from .clients.mediawiki import MediaWiki
-from .clients.mediawiki_legacy import MediaWiki_Legacy
-from .clients.mediawiki_lurk import MediaWiki_Lurk
+from dataclasses import dataclass, field
 
-
-from .clients.base import BaseClient
+from .clients import BaseClient, MediaWiki, MediaWiki_Lurk
 from .drivers import HttpxDriver
-
-from .exceptions import NotFound
+from .models import URL
+from .exceptions import NotFound  # noqa
 
 
+@dataclass
 class Wikipya:
-    def __init__(
-        self,
-        lang=None,
-        url="https://{lang}.wikipedia.org/w/api.php",
-        params={
-            "format": "json",
-            "action": "query",
-            "formatversion": 2
-        },
-        host="",
-        prefix="/w",
-        client=MediaWiki,
-        version=None,  # unsupported,
-        lurk=True,
-        **kwargs
-    ):
-        self.url = url.format(lang=lang)
-        self.prefix = prefix
-
-        client = MediaWiki_Lurk if lurk else client
-        client = client(HttpxDriver, self.url)
-
-        methods = [func for func in dir(BaseClient)
-                   if callable(getattr(BaseClient, func)) and
-                   not func.startswith("__")]
-
-        for method in methods:
-            setattr(self, method, getattr(client, method))
+    lang: str = "ru"
+    base_url: str = "https://{lang}.wikipedia.org/w/api.php"
+    prefix: str = "/w"
+
+    client: BaseClient = MediaWiki
+    is_lurk: bool = False
+
+    params: dict = field(default_factory=dict)
+
+    def __post_init__(self):
+        self.url = URL(self.base_url, self.lang, self.prefix)
+
+        self.client = MediaWiki_Lurk if self.is_lurk else self.client
+        self.client = self.client(
+            url=self.url,
+            driver=HttpxDriver,
+
+            **self.params
+        )
+
+    def get_instance(self) -> MediaWiki:
+        return self.client
diff --git a/wikipya/clients.py b/wikipya/clients.py
new file mode 100644
index 0000000..7ae7d3f
--- /dev/null
+++ b/wikipya/clients.py
@@ -0,0 +1,42 @@
+from dataclasses import dataclass, field
+
+from .drivers import BaseDriver, HttpxDriver
+from .constants import TAG_BLOCKLIST, IMAGE_BLOCKLIST, DEFAULT_PARAMS
+
+
+@dataclass
+class BaseClient:
+    url: str
+    driver: BaseDriver = field(repr=False, default_factory=HttpxDriver)
+
+    tag_blocklist: list = field(repr=False, default_factory=lambda: TAG_BLOCKLIST)
+    img_blocklist: list = field(repr=False, default_factory=lambda: IMAGE_BLOCKLIST)
+
+    default_params: dict = field(repr=False, default_factory=lambda: DEFAULT_PARAMS)
+
+    def __post_init__(self):
+        self.driver = self.driver(self.url, params=self.default_params)
+
+
+@dataclass
+class MediaWiki(BaseClient):
+    from .methods import (
+        get_all,
+        get_page_name,
+        image,
+        opensearch,
+        page,
+        search,
+        summary,
+        sections
+    )
+
+
+@dataclass
+class MediaWiki_Legacy(MediaWiki):
+    from .methods import get_images_list, get_image, legacy_image as image
+
+
+@dataclass
+class MediaWiki_Lurk(MediaWiki_Legacy):
+    pass
diff --git a/wikipya/clients/base.py b/wikipya/clients/base.py
deleted file mode 100644
index 4353216..0000000
--- a/wikipya/clients/base.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from ..drivers import HttpxDriver
-
-
-class BaseClient:
-    def __init__(self, driver=HttpxDriver, url=None, lang="ru"):
-        self.driver = driver(
-            (url or self.BASE_URL).format(lang=lang),
-            params=self.DEFAULT_PARAMS
-        )
-
-    async def search(self, *args, **kwargs):
-        raise NotImplementedError
-
-    async def opensearch(self, *args, **kwargs):
-        raise NotImplementedError
-
-    async def page(self, *args, **kwargs):
-        raise NotImplementedError
-
-    async def _page(self, *args, **kwargs):
-        raise NotImplementedError
-
-    async def image(self, *args, **kwargs):
-        raise NotImplementedError
-
-    async def get_all(self, *args, **kwargs):
-        raise NotImplementedError
-
-    async def getPageName(self, *args, **kwargs):
-        raise NotImplementedError
\ No newline at end of file
diff --git a/wikipya/clients/mediawiki.py b/wikipya/clients/mediawiki.py
deleted file mode 100644
index 27a0555..0000000
--- a/wikipya/clients/mediawiki.py
+++ /dev/null
@@ -1,130 +0,0 @@
-from .base import BaseClient
-from ..types import SearchItem, WikipyaPage, Image, OpenSearchResult
-
-from ..exceptions import NotFound
-
-
-class MediaWiki(BaseClient):
-    DEFAULT_PARAMS = {
-        "format": "json",
-        "action": "query",
-        "formatversion": 2
-    }
-
-    BASE_URL = "https://{lang}.wikipedia.org/w/api.php"
-    LANG = None
-
-    WGR_FLAG = (
-        "https://upload.wikimedia.org/wikipedia/commons/thumb" +
-        "/8/85/Flag_of_Belarus.svg/1000px-Flag_of_Belarus.svg.png"
-    )
-
-    WRW_FLAG = (
-        "https://upload.wikimedia.org/wikipedia/commons/thumb" +
-        "/5/50/Flag_of_Belarus_%281918%2C_1991%E2%80%931995%29.svg" +
-        "/1000px-Flag_of_Belarus_%281918%2C_1991%E2%80%931995%29.svg.png"
-    )
-
-    async def search(self, query, limit=1, prop="size"):
-        res = await self.driver.get(
-            list="search",
-            srsearch=query,
-            srlimit=limit,
-            srprop=prop
-        )
-
-        if len(res.query.search) == 0:
-            raise NotFound("Search can't find anything on your request")
-
-        return [SearchItem(
-            title=item.__dict__.get("title"),
-            pageid=item.__dict__.get("pageid")
-        ) for item in res.query.search]
-
-    async def opensearch(self, query, limit=1):
-        res = OpenSearchResult(*await self.driver.get(
-            action="opensearch",
-            search=query,
-            limit=limit,
-        ))
-
-        if len(res.variants) == 0:
-            raise NotFound("OpenSearch can't find anything on your request")
-
-        return res
-
-    async def page(self, query, section=0, prop="text", blocklist=()):
-        if query.__class__ == str:
-            params = {"page": query}
-
-        elif query.__class__ == int:
-            params = {"pageid": query}
-
-        elif query.pageid is not None:
-            params = {"pageid": query.pageid}
-
-        else:
-            params = {"page": query.title}
-
-        res = await self.driver.get(
-            action="parse",
-            section=section,
-            prop=prop,
-            redirects="true",
-            **params,
-        )
-
-        return WikipyaPage(res.parse, lang=self.LANG, _image=self.image,
-                           tag_blocklist=blocklist)
-
-    async def image(self, titles, pithumbsize=1000,
-                    piprop="thumbnail", img_blocklist=(), prefix=None):
-        res = await self.driver.get(
-            titles=titles,
-            prop="pageimages",
-            pilicense="any",
-            pithumbsize=pithumbsize,
-            piprop=piprop,
-        )
-
-        try:
-            image = res.query.pages[-1]
-            thumb = image.thumbnail
-
-            return Image(**thumb.__dict__)
-
-        except AttributeError:
-            raise NotFound("Not found image")
-
-    async def getPageName(self, id_):
-        res = await self.driver.get(
-            pageids=id_
-        )
-
-        try:
-            return res.query.pages[-1].title
-        except AttributeError:
-            raise NotFound(f"Not found page with this id: {id_}")
-
-    async def get_all(self, query, lurk=False,
-                      blocklist=(), prefix="w", img_blocklist=(), **kwargs):
-        search = await self.search(query)
-        opensearch = await self.opensearch(
-            query if lurk else search[0].title
-        )
-
-        variant = opensearch.variants[0]
-        page = await self.page(variant.title, blocklist=blocklist)
-
-        try:
-            image = await page.image(prefix=prefix,
-                                     img_blocklist=img_blocklist)
-            image = image.source
-
-        except Exception as e:
-            image = -1
-
-        if image == self.WGR_FLAG:
-            image = self.WRW_FLAG
-
-        return page, image, variant.link
diff --git a/wikipya/clients/mediawiki_legacy.py b/wikipya/clients/mediawiki_legacy.py
deleted file mode 100644
index 9d1ca82..0000000
--- a/wikipya/clients/mediawiki_legacy.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from .mediawiki import MediaWiki
-from ..types import SearchItem, WikipyaPage, Image
-from ..exceptions import NotFound
-
-from bs4 import BeautifulSoup
-
-
-class MediaWiki_Legacy(MediaWiki):
-    DEFAULT_PARAMS = {
-        "format": "json",
-        "action": "query",
-        "formatversion": 2
-    }
-
-    LANG = None
-
-    async def image(self, titles, pithumbsize=1000,
-                    piprop="thumbnail", img_blocklist=(),
-                    prefix="/w", **kwargs):
-        data = await self.driver.get(
-            action="parse",
-            page=titles,
-            prop="images",
-            redirects="true",
-            section=0
-        )
-
-        all_images = data.parse.images
-
-        if len(all_images) == 0:
-            raise NotFound("Not found images")
-        
-        images = []
-
-        for image in all_images:
-            if image in img_blocklist:
-                continue
-
-            try:
-                images.append(await self.get_image(image, prefix=prefix))
-                return images[0]
-            except Exception as e:
-                print(e)
-
-    async def get_image(self, name, prefix="/w"):
-        url = self.driver.url.lower() \
-                             .replace('/wiki/api.php', prefix) \
-                             .replace("/w/api.php", prefix) \
-                             .replace("/api.php", prefix)
-
-        status, data, url = await self.driver.get_html(
-            f"{url}/File:{name}"
-        )
-
-        soup = BeautifulSoup(data, 'lxml')
-
-        file = soup.find("div", id="file")
-        thumbnails = soup.find_all("img", {"class": "pi-image-thumbnail"})
-        thumbs = soup.find_all("figure", {"class": "thumb"})
-        navigation = soup.find_all("nav", {"class": "pi-navigation"})
-
-        if file is not None:
-            image = file.a.img
-            image["src"] = f"https:{image['src']}"
-
-        elif len(thumbnails) != 0:
-            image = thumbnails[0]
-            image["src"] = image["srcset"].split()[-2]
-
-        elif len(navigation) != 0:
-            image = navigation[0].a.img
-
-        elif len(thumbs) != 0:
-            image = thumbs[0].a.img
-
-        else:
-            raise NotFound("Not found image")
-
-        return Image(
-            source=image["src"],
-            width=int(image["width"]), height=int(image["height"])
-        )
diff --git a/wikipya/clients/mediawiki_lurk.py b/wikipya/clients/mediawiki_lurk.py
deleted file mode 100644
index 62beb75..0000000
--- a/wikipya/clients/mediawiki_lurk.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from .mediawiki_legacy import MediaWiki_Legacy
-from ..types import SearchItem, WikipyaPage, Image
-from ..exceptions import NotFound
-
-
-class MediaWiki_Lurk(MediaWiki_Legacy):
-    DEFAULT_PARAMS = {
-        "format": "json",
-        "action": "query",
-        "formatversion": 2
-    }
-
-    LANG = None
diff --git a/wikipya/constants.py b/wikipya/constants.py
new file mode 100644
index 0000000..651596b
--- /dev/null
+++ b/wikipya/constants.py
@@ -0,0 +1,125 @@
+TAG_BLOCKLIST = [
+    ["ol", {"class": "references"}],
+    [None, {"class": "reference"}],
+    ["link"],
+    ["style"],
+    ["img"],
+    ["aside"],
+    ["table"],
+    ["br"],
+    ["span", {"class": "mw-ext-cite-error"}],
+    ["div", {"class": "thumbinner"}],
+    ["div", {"class": "gallerytext"}],
+    ["div", {"class": "archwiki-template-meta-related-articles-start"}],
+    ["div", {"class": "tright"}],
+    ["div", {"class": "plainlist"}],
+    ["div", {"class": "gametabs"}],
+    [None, {"class": "noprint"}],
+    ["span", {"id": "w4g_rb_area-1"}],
+    ["p", {"class": "caption"}],
+]
+
+
+IMAGE_BLOCKLIST = [
+    "Gtk-redo-ltr.svg",
+    "48ef4db2ad.png",
+    "Pen_and_book.jpg",
+    "Lurkomoring.png",
+    "WALL_OF_TEXT.jpg",
+    "Attention32.png",
+    "AngryMonkey.png",
+    "Lentaru.png",
+    "Refresh.png",
+    "Barbed_wire_new_flip.png",
+    "APNGball.png",
+    "Important.png",
+    "Chaos_star_transp.png",
+    "Krasiviy_petushok.jpg",
+    "Hypnotoad_1.gif",
+    "ZOG1.jpg",
+    "Brick_one.jpg",
+    "Rav.jpg",
+    "Матановая_капча1.png",
+    "Drama.png",
+    "Notcrap_small.png",
+    "Tomat.jpg",
+    "Trash.PNG",
+    "Raksmall.gif",
+    "A.S._Pushkin.jpg",
+    "Shkolota.jpg",
+    "520px-Diamond-caution.svg.png",
+    "Miseri.png",
+    "Banhammer.png",
+    "Dyhota.png",
+    "Боевой_треножник.jpg",
+    "Huy.png",
+    "Protect.svg",
+    "1259141754733.jpg",
+    "Blood_jar.png",
+    "Big_lie.png",
+    "Warning_1.png",
+    "Tema_sm.jpg",
+    "Boroda.jpeg",
+    "Post-negrov-small.jpg",
+    "Advicedog.jpg",
+    "Renamed.png",
+    "Renamed_self.png",
+    "0667d021c975.jpg",
+    "Nihuya-small.png",
+    "Tema_sm_blind.jpg",
+    "Not_found_2.svg",
+    "GN_small2.png",
+    "Kash.png",
+    "UnLurkomoring.PNG",
+    "Hate_small.png",
+    "Nohate.jpg",
+    "RedHate.png",
+    "Mergearticles.png",
+    "Cut.png",
+    "19_md_house.jpg",
+    "Linkota.jpg",
+    "Нет.png",
+    "Dont_feed_the_troll_small.png",
+    "Slowpoke.png",
+    "Smile.svg",
+    "St.jpg",
+    "Petrosyan.png",
+    "Wrar64.png",
+    "Кунсткамера.png",
+    "Capsula.png",
+    "Politota.png",
+    "10к.gif",
+    "Kapitan_ochevidnost'.jpg",
+    "Facepalm_kyon_sq.jpg",
+    "Eri-x-Yakumo.jpg",
+    "Свиборг.png",
+    "Butthurt.png",
+    "Aalien_pony.png",
+    "Uvaga.gif",
+    "Ae_logo_smallfile.png",
+    "Nestle.png",
+    "Emblem-important.svg",
+    "Stroke.png",
+    "Necronomicon.png",
+    "Ug.png",
+    "Spoiler_alert.jpg",
+    "Tuesday,_2_February.png",
+    "Recycle.png",
+    "Ammonite,_Quenstedtoceras_paucicostatum.jpg",
+    "Dont_drink_1.jpg",
+]
+
+
+DEFAULT_PARAMS = {"format": "json", "action": "query", "formatversion": 2}
+
+
+WGR_FLAG = (
+    "https://upload.wikimedia.org/wikipedia/commons/thumb"
+    + "/8/85/Flag_of_Belarus.svg/1000px-Flag_of_Belarus.svg.png"
+)
+
+WRW_FLAG = (
+    "https://upload.wikimedia.org/wikipedia/commons/thumb"
+    + "/5/50/Flag_of_Belarus_%281918%2C_1991%E2%80%931995%29.svg"
+    + "/1000px-Flag_of_Belarus_%281918%2C_1991%E2%80%931995%29.svg.png"
+)
diff --git a/wikipya/drivers.py b/wikipya/drivers.py
index cc95fbb..6a947ea 100644
--- a/wikipya/drivers.py
+++ b/wikipya/drivers.py
@@ -1,103 +1,37 @@
-try:
-    import aiohttp
-except:
-    pass
-
 try:
     import httpx
-except:
-    pass
+except ImportError:
+    print("Not found httpx. Try pip install httpx")
 
-import time
-import json
+from dataclasses import dataclass, field
+from typing import Optional
 
 from .exceptions import ParseError
 
 
-class JSONObject:
-    """JSON => Class"""
-    def __init__(self, dict):
-        self.add(dict)
-
-    def add(self, dict):
-        vars(self).update(dict)
-
-
+@dataclass
 class BaseDriver:
-    def __init__(self, url="example.com", timeout=5, params=()):
-        self.timeout = timeout
-        self.params = params
-        self.url = url
+    url: str = "example.org"
+    timeout: Optional[int] = 5
+    params: dict = field(default_factory=dict)
 
     async def get(self, url, params=(), timeout=None):
         raise NotImplementedError
 
 
-class AiohttpDriver(BaseDriver):
-    async def get(self, url=None, timeout=None, debug=False, **params):
-        if len(list(params.keys())) == 0:
-            params = None
-        else:
-            params = {**self.params, **params}
-
-        async with aiohttp.ClientSession() as session:
-            start = time.time()
-
-            async with session.get(
-                url or self.url, params=params,
-                timeout=timeout or self.timeout
-            ) as response:
-                text = await response.text()
-
-                if debug:
-                    print(response.url)
-                    print(time.time() - start)
-
-                js = json.loads(text, object_hook=JSONObject)
-
-                if isinstance(js, list):
-                    pass
-
-                elif js.__dict__.get("error") is not None:
-                    raise ParseError(f"{js.error.code}: {js.error.info}")
-
-                return js
-
-    async def get_html(self, url=None, timeout=None, debug=False, **params):
-        if len(list(params.keys())) == 0:
-            params = None
-        else:
-            params = {**self.params, **params}
-
-        async with aiohttp.ClientSession() as session:
-            start = time.time()
-
-            async with session.get(
-                url or self.url, params=params,
-                timeout=timeout or self.timeout
-            ) as response:
-                text = await response.text()
-
-                if debug:
-                    print(response.url)
-                    print(time.time() - start)
-
-                return response.status, text, response.url
-
-
+@dataclass
 class HttpxDriver(BaseDriver):
     async def get(self, url=None, timeout=None, debug=False, **params):
-        async with httpx.AsyncClient(timeout=timeout) as client:
-            res = await client.get(url or self.url,
-                                   params={**self.params, **params})
-            js = json.loads(res.text, object_hook=JSONObject)
+        async with httpx.AsyncClient(timeout=self.timeout) as client:
+            res = await client.get(str(url or self.url), params={**self.params, **params})
+
+        res.json = res.json()
 
-        if not isinstance(js, list) and res.json().get("error"):
-            raise ParseError(f"{js.error.code}: {js.error.info}")
+        if not isinstance(res.json, list) and (error := res.json.get("error")):
+            raise ParseError(f'{error["code"]}: {error["info"]}')
 
-        return js
+        return res
 
     async def get_html(self, url=None, timeout=None, debug=False, **params):
-        async with httpx.AsyncClient(timeout=timeout) as client:
-            res = await client.get(url or self.url, params=params)
-            return res.status_code, res.text, res.url
+        async with httpx.AsyncClient(timeout=self.timeout) as client:
+            return await client.get(str(url or self.url), params=params, follow_redirects=True)
diff --git a/wikipya/methods/__init__.py b/wikipya/methods/__init__.py
new file mode 100644
index 0000000..9676b97
--- /dev/null
+++ b/wikipya/methods/__init__.py
@@ -0,0 +1,11 @@
+# flake8: noqa
+
+from .get_all import get_all
+from .get_page_name import get_page_name
+from .image_legacy import *
+from .image import image
+from .opensearch import opensearch
+from .page import page
+from .search import search
+from .summary import summary
+from .sections import sections
diff --git a/wikipya/methods/get_all.py b/wikipya/methods/get_all.py
new file mode 100644
index 0000000..ad9848e
--- /dev/null
+++ b/wikipya/methods/get_all.py
@@ -0,0 +1,20 @@
+from ..constants import WGR_FLAG, WRW_FLAG
+
+
+async def get_all(self, query, lurk=False):
+    search = await self.search(query)
+    opensearch = await self.opensearch(
+        query if lurk else search[0].title
+    )
+
+    result = opensearch.results[0]
+    page = await self.page(result.title)
+
+    try:
+        image = await self.image(page.title)
+        image = WRW_FLAG if image.source == WGR_FLAG else image.source
+
+    except Exception:
+        image = -1
+
+    return page, image, result.link
diff --git a/wikipya/methods/get_page_name.py b/wikipya/methods/get_page_name.py
new file mode 100644
index 0000000..7565394
--- /dev/null
+++ b/wikipya/methods/get_page_name.py
@@ -0,0 +1,10 @@
+from ..exceptions import NotFound
+
+
+async def get_page_name(self, id) -> str:
+    res = await self.driver.get(pageids=id)
+
+    try:
+        return res.json["query"]["pages"][-1]["title"]
+    except AttributeError:
+        raise NotFound(f"Not found page with this id: {id}")
diff --git a/wikipya/methods/image.py b/wikipya/methods/image.py
new file mode 100644
index 0000000..43e50fc
--- /dev/null
+++ b/wikipya/methods/image.py
@@ -0,0 +1,20 @@
+from ..models import Image
+from ..exceptions import NotFound
+
+
+async def image(self, titles, pithumbsize=1000) -> Image:
+    res = await self.driver.get(
+        titles=titles,
+        prop="pageimages",
+        pilicense="any",
+        pithumbsize=pithumbsize,
+        piprop="thumbnail",
+    )
+
+    try:
+        image = res.json["query"]["pages"][-1]
+        thumb = image["thumbnail"]
+
+        return Image(**thumb)
+    except AttributeError:
+        raise NotFound("Not found image")
diff --git a/wikipya/methods/image_legacy.py b/wikipya/methods/image_legacy.py
new file mode 100644
index 0000000..a06ec2b
--- /dev/null
+++ b/wikipya/methods/image_legacy.py
@@ -0,0 +1,69 @@
+from bs4 import BeautifulSoup
+
+from ..models import Image, Images
+from ..exceptions import NotFound
+
+
+__all__ = ("legacy_image", "get_images_list", "get_image")
+
+
+async def legacy_image(self, titles, pithumbsize=1000) -> Image:
+    _images = await self.get_images_list(titles)
+    images = []
+
+    if len(_images) == 0:
+        raise NotFound("Not found images")
+
+    for image in _images:
+        if image in self.img_blocklist:
+            continue
+
+        try:
+            images.append(await self.get_image(image))
+            return images[0]
+        except Exception as e:
+            print(e)
+
+
+async def get_images_list(self, titles) -> list[str]:
+    r = await self.driver.get(
+        action="parse",
+        page=titles,
+        prop="images",
+        redirects="true",
+        section=0
+    )
+
+    return Images.parse_obj(r.json["parse"]).images
+
+
+async def get_image(self, name) -> Image:
+    r = await self.driver.get_html(f"{self.url.image_url}/File:{name}")
+    soup = BeautifulSoup(r.text, 'lxml')
+
+    file = soup.find("div", id="file")
+    thumbnails = soup.find_all("img", {"class": "pi-image-thumbnail"})
+    thumbs = soup.find_all("figure", {"class": "thumb"})
+    navigation = soup.find_all("nav", {"class": "pi-navigation"})
+
+    if file is not None:
+        image = file.a.img
+        image["src"] = f"https:{image['src']}"
+
+    elif len(thumbnails) != 0:
+        image = thumbnails[0]
+        image["src"] = image["srcset"].split()[-2]
+
+    elif len(navigation) != 0:
+        image = navigation[0].a.img
+
+    elif len(thumbs) != 0:
+        image = thumbs[0].a.img
+
+    else:
+        raise NotFound("Not found image")
+
+    return Image(
+        source=image["src"],
+        width=int(image["width"]), height=int(image["height"])
+    )
diff --git a/wikipya/methods/lib.py b/wikipya/methods/lib.py
new file mode 100644
index 0000000..3c3dfaf
--- /dev/null
+++ b/wikipya/methods/lib.py
@@ -0,0 +1,13 @@
+from typing import Union
+from ..models import SearchResult
+
+
+def query2param(query: Union[str, int, SearchResult]) -> dict:
+    if query.__class__ == str:
+        return {"page": query}
+    elif query.__class__ == int:
+        return {"pageid": query}
+    elif query.pageid is not None:
+        return {"pageid": query.pageid}
+    else:
+        return {"page": query.title}
diff --git a/wikipya/methods/opensearch.py b/wikipya/methods/opensearch.py
new file mode 100644
index 0000000..a3f2069
--- /dev/null
+++ b/wikipya/methods/opensearch.py
@@ -0,0 +1,17 @@
+from ..models import OpenSearch
+from ..exceptions import NotFound
+
+
+async def opensearch(self, query, limit=1) -> OpenSearch:
+    r = await self.driver.get(
+        action="opensearch",
+        search=query,
+        limit=limit,
+    )
+
+    results = OpenSearch(*r.json)
+
+    if len(results.results) == 0:
+        raise NotFound("OpenSearch can't find anything on your request")
+
+    return results
diff --git a/wikipya/methods/page.py b/wikipya/methods/page.py
new file mode 100644
index 0000000..9e1819c
--- /dev/null
+++ b/wikipya/methods/page.py
@@ -0,0 +1,22 @@
+from ..models import Page
+from .lib import query2param
+
+
+async def page(self, query, section=0, prop="text") -> Page:
+    res = await self.driver.get(
+        action="parse",
+        section=section,
+        prop=prop,
+        redirects="true",
+        **query2param(query),
+    )
+
+    try:
+        res.json["parse"]["text"] = res.json["parse"]["text"]["*"]
+    except Exception:
+        pass
+
+    page = Page.parse_obj(res.json["parse"])
+    page.tag_blocklist = self.tag_blocklist
+
+    return page
diff --git a/wikipya/methods/search.py b/wikipya/methods/search.py
new file mode 100644
index 0000000..934ec78
--- /dev/null
+++ b/wikipya/methods/search.py
@@ -0,0 +1,18 @@
+from ..models import Search, SearchResult
+from ..exceptions import NotFound
+
+
+async def search(self, query, limit=1, prop="snippet") -> list[SearchResult]:
+    res = await self.driver.get(
+        list="search",
+        srsearch=query,
+        srlimit=limit,
+        srprop=prop
+    )
+
+    results = Search.parse_obj(res.json["query"]["search"]).__root__
+
+    if len(results) == 0:
+        raise NotFound("Search can't find anything on your request")
+    else:
+        return results
diff --git a/wikipya/methods/sections.py b/wikipya/methods/sections.py
new file mode 100644
index 0000000..86510a2
--- /dev/null
+++ b/wikipya/methods/sections.py
@@ -0,0 +1,14 @@
+from .lib import query2param
+from ..models import Page
+
+
+async def sections(self, query) -> Page:
+    res = await self.driver.get(
+        action="parse",
+        prop="sections",
+        redirects="true",
+        **query2param(query),
+    )
+
+    page = Page.parse_obj(res.json["parse"])
+    return page
diff --git a/wikipya/methods/summary.py b/wikipya/methods/summary.py
new file mode 100644
index 0000000..9c51c3f
--- /dev/null
+++ b/wikipya/methods/summary.py
@@ -0,0 +1,8 @@
+from ..models import Summary
+
+
+async def summary(self, title) -> Summary:
+    res = await self.driver.get_html(
+        f"{self.driver.url.cleaned}api/rest_v1/page/summary/{title}")
+
+    return Summary.parse_raw(res.text)
diff --git a/wikipya/models/__init__.py b/wikipya/models/__init__.py
new file mode 100644
index 0000000..1629756
--- /dev/null
+++ b/wikipya/models/__init__.py
@@ -0,0 +1,19 @@
+from .page import Page
+from .image import Image
+from .images import Images
+from .search import Search, SearchResult
+from .opensearch import OpenSearch, OpenSearchResult
+from .url import URL
+from .summary import Summary
+from .section import Section
+
+
+__all__ = (
+    "Page",
+    "Image", "Images",
+    "Search", "SearchResult",
+    "OpenSearch", "OpenSearchResult",
+    "URL",
+    "Summary",
+    "Section"
+)
diff --git a/wikipya/models/image.py b/wikipya/models/image.py
new file mode 100644
index 0000000..4c7ba09
--- /dev/null
+++ b/wikipya/models/image.py
@@ -0,0 +1,8 @@
+from pydantic import BaseModel
+from typing import Optional
+
+
+class Image(BaseModel):
+    source: str
+    width: Optional[int]
+    height: Optional[int]
diff --git a/wikipya/models/images.py b/wikipya/models/images.py
new file mode 100644
index 0000000..e5e30a7
--- /dev/null
+++ b/wikipya/models/images.py
@@ -0,0 +1,6 @@
+from pydantic import BaseModel
+
+
+class Images(BaseModel):
+    title: str
+    images: list[str]
diff --git a/wikipya/models/opensearch.py b/wikipya/models/opensearch.py
new file mode 100644
index 0000000..4259d38
--- /dev/null
+++ b/wikipya/models/opensearch.py
@@ -0,0 +1,23 @@
+from dataclasses import dataclass, field
+from typing import Any
+
+
+@dataclass
+class OpenSearch:
+    query: str
+    _variants: list = field(default_factory=list, repr=False)
+    descriptions: list = field(default_factory=list, repr=False)
+    links: list = field(default_factory=list, repr=False)
+    results: Any = field(default_factory=list)
+
+    def __post_init__(self):
+        for i, variant in enumerate(self._variants):
+            link = self.links[i] if len(self.links) > 0 else None
+
+            self.results.append(OpenSearchResult(variant, link))
+
+
+@dataclass
+class OpenSearchResult:
+    title: str
+    link: str = None
diff --git a/wikipya/models/page.py b/wikipya/models/page.py
new file mode 100644
index 0000000..1e6bfdd
--- /dev/null
+++ b/wikipya/models/page.py
@@ -0,0 +1,39 @@
+from pydantic import BaseModel, Field
+from tghtml import TgHTML
+
+from bs4 import BeautifulSoup
+
+from typing import Optional
+
+from .section import Section
+
+
+class Page(BaseModel):
+    title: str
+    pageid: Optional[int]
+
+    text: Optional[str]
+    sections: Optional[list[Section]]
+
+    tag_blocklist: Optional[list] = Field(repr=False)
+
+    @property
+    def parsed(self):
+        return TgHTML(self.text, self.tag_blocklist).parsed.strip()
+
+    @property
+    def fixed(self):
+        print("Page.fixed is deprecated, please use .parsed!")
+        return self.parsed
+
+    @property
+    def section_name(self):
+        soup = BeautifulSoup(self.text, "lxml")
+        spans = soup.find_all("span")
+
+        for span in spans:
+            if span.text != "":
+                return span.text
+
+            elif not span["id"].startswith("."):
+                return span["id"]
diff --git a/wikipya/models/search.py b/wikipya/models/search.py
new file mode 100644
index 0000000..f7cfd4e
--- /dev/null
+++ b/wikipya/models/search.py
@@ -0,0 +1,13 @@
+from pydantic import BaseModel, Field
+from typing import Optional
+
+
+class SearchResult(BaseModel):
+    title: str
+    snippet: Optional[str]
+    size: Optional[int]
+    page_id: Optional[int] = Field(alias="pageid")
+
+
+class Search(BaseModel):
+    __root__: list[SearchResult]
diff --git a/wikipya/models/section.py b/wikipya/models/section.py
new file mode 100644
index 0000000..bf07297
--- /dev/null
+++ b/wikipya/models/section.py
@@ -0,0 +1,10 @@
+from pydantic import BaseModel, Field
+
+
+class Section(BaseModel):
+    title: str = Field(alias="line")
+    number: str
+    index: int
+    level: str
+    from_title: str = Field(alias="fromtitle")
+    bytes_offset: int = Field(alias="byteoffset")
diff --git a/wikipya/models/summary.py b/wikipya/models/summary.py
new file mode 100644
index 0000000..d60f070
--- /dev/null
+++ b/wikipya/models/summary.py
@@ -0,0 +1,27 @@
+from pydantic import BaseModel, Field
+from typing import Optional
+
+from .image import Image
+
+
+class DesktopUrls(BaseModel):
+    page: str
+
+
+class ContentUrls(BaseModel):
+    desktop: DesktopUrls
+
+
+class Summary(BaseModel):
+    title: str
+    page_id: int = Field(alias="pageid")
+
+    thumbnail: Optional[Image]
+    original_image: Optional[Image] = Field(alias="originalimage")
+
+    description: Optional[str]
+
+    extract: str
+    extract_html: str
+
+    content_urls: ContentUrls
diff --git a/wikipya/models/url.py b/wikipya/models/url.py
new file mode 100644
index 0000000..00c9933
--- /dev/null
+++ b/wikipya/models/url.py
@@ -0,0 +1,26 @@
+import re
+
+from dataclasses import dataclass
+from typing import Optional
+
+
+@dataclass
+class URL:
+    base_url: str = "https://{lang}.wikipedia.org/w/api.php"
+    lang: Optional[str] = "ru"
+    prefix: str = "/w"
+
+    def __str__(self):
+        return self.url
+
+    @property
+    def url(self):
+        return self.base_url.format(lang=self.lang)
+
+    @property
+    def image_url(self):
+        return re.sub(r"(wiki|w)?/api.php", self.prefix, self.url)
+
+    @property
+    def cleaned(self):
+        return re.sub(r"(wiki|w)?/api.php", "", self.url)
diff --git a/wikipya/types.py b/wikipya/types.py
deleted file mode 100644
index 50d7105..0000000
--- a/wikipya/types.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from dataclasses import dataclass, field
-from typing import Any
-
-from tghtml import TgHTML
-
-from .drivers import JSONObject
-
-
-@dataclass
-class SearchItem:
-    title: str
-    pageid: int
-
-
-@dataclass
-class OpenSearchResult:
-    query: str
-    _variants: list
-    unknown: list = field(default_factory=list)
-    links: list = field(default_factory=list)
-    variants: Any = field(default_factory=list)
-
-    def __post_init__(self):
-        for i, variant in enumerate(self._variants):
-            link = self.links[i] if len(self.links) > 0 else None
-
-            self.variants.append(OpenSearchItem(variant, link))
-
-
-@dataclass
-class OpenSearchItem:
-    title: str
-    link: str = None
-
-
-@dataclass
-class Image:
-    source: str
-    width: int
-    height: int
-
-    def __post_init__(self):
-        if self.source.find(".gif") != -1:
-            raise TypeError("Gif file")
-
-        if self.source.find("revision/latest/scale-to-width-down") != -1:
-            revision = self.source.split("/revision")
-            rev = revision[1].split("?")[0]
-            size = rev.split("/")[-1]
-
-            new_rev = rev.replace(str(size), "10000")
-
-            self.source = self.source.replace(rev, new_rev)
-
-
-@dataclass
-class WikipyaPage:
-    parse: JSONObject
-    lang: str = "en"
-    tag_blocklist: tuple = ()
-    img_blocklist: tuple = ()
-    _image: Any = None
-
-    def __post_init__(self):
-        vars(self).update(self.parse.__dict__)
-
-        # fix for lurkmore
-        if self.text.__class__.__name__ == "JSONObject":
-            self.text = self.text.__dict__["*"]
-
-    async def image(self, *args, **kwargs):
-        return await self._image(self.title, *args, **kwargs)
-
-    @property
-    def parsed(self):
-        return str(TgHTML(self.text, self.tag_blocklist, is_wikipedia=False))
-
-    @property
-    def fixed(self):
-        namelist = [
-            ["Белоруссия", "Беларусь"],
-            ["Белоруссии", "Беларуси"],
-            ["Беларуссию", "Беларусь"],
-            ["Белоруссией", "Беларусью"],
-            ["Белоруссиею", "Беларусью"],
-
-            ["Белору́ссия", "Белару́сь"],
-            ["Белору́ссии", "Белару́си"],
-            ["Белору́ссию", "Белару́сь"],
-            ["Белору́ссией", "Белару́сью"],
-            ["Белору́ссиею", "Белару́сью"],
-
-
-            ["на Украин", "в Украин"],
-        ]
-
-        for name in namelist:
-            text = self.parsed.replace(*name)
-
-        return text
\ No newline at end of file


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..cd2b0a3
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,74 @@
+import pytest
+from wikipya import Wikipya
+from wikipya.clients import MediaWiki_Legacy
+from pytest_lazyfixture import lazy_fixture
+
+from params import Params
+
+
+@pytest.fixture(params=[
+    lazy_fixture("lurkmore"),
+    lazy_fixture("wikipedia"),
+    lazy_fixture("fallout"),
+    lazy_fixture("kaiserreich")
+])
+def params(request):
+    return request.param
+
+
+@pytest.fixture(scope="class")
+def lurkmore():
+    """Went lurkmore instance"""
+
+    return Params(
+        client=Wikipya(
+            base_url="https://ipv6.lurkmo.re/api.php",
+            prefix="",
+            # img_blocklist=blocklist["images"]
+            is_lurk=True
+        ).get_instance(),
+
+        search_limit=1,
+        search_query="эта страна",
+        image_query="Украина"
+    )
+
+
+@pytest.fixture(scope="class")
+def wikipedia():
+    """Went wikipedia instance"""
+
+    return Params(
+        client=Wikipya("ru").get_instance(),
+    )
+
+
+@pytest.fixture(scope="class")
+def fallout():
+    """Went fallout instance"""
+
+    return Params(
+        client=Wikipya(
+            base_url="https://fallout.fandom.com/ru/api.php",
+            prefix="",
+            client=MediaWiki_Legacy
+        ).get_instance(),
+        search_query="Марипоза",
+        search_limit=1,
+        image_query="Стрип"
+    )
+
+
+@pytest.fixture(scope="class")
+def kaiserreich():
+    """Went kaiserreich instance"""
+
+    return Params(
+        client=Wikipya(
+            base_url="https://kaiserreich.fandom.com/ru/api.php",
+            prefix="",
+            client=MediaWiki_Legacy
+        ).get_instance(),
+        search_query="Германская империя",
+        search_limit=1
+    )
diff --git a/tests/params.py b/tests/params.py
new file mode 100644
index 0000000..d5c9228
--- /dev/null
+++ b/tests/params.py
@@ -0,0 +1,15 @@
+from dataclasses import dataclass
+
+from wikipya import Wikipya
+
+
+@dataclass
+class Params:
+    client: Wikipya
+
+    allow_http: bool = False
+    check_image_res: bool = True
+
+    search_limit: int = 4
+    search_query: str = "cmake"
+    image_query: str = "Украина"
diff --git a/tests/test_common.py b/tests/test_common.py
deleted file mode 100644
index 83c9e14..0000000
--- a/tests/test_common.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import pytest
-
-
-class CommonTestsWikipya:
-    ALLOW_HTTP = False
-    SEARCH_QUERY = "cmake"
-    SEARCH_LIMIT = 4
-    IMAGE_QUERY = "Украина"
-    CHECK_IMAGE_RES = True
-
-    def test_https(self):
-        # Use HTTPS instead of HTTP, please. Ignore with ALLOW_HTTP
-
-        is_https_used = self.wikipya.url.startswith("https://")
-        assert self.ALLOW_HTTP or is_https_used
-
-    @pytest.mark.asyncio
-    async def test_search(self):
-        results = await self.wikipya.search(self.SEARCH_QUERY,
-                                            limit=self.SEARCH_LIMIT)
-
-        assert len(results) == self.SEARCH_LIMIT
-
-    @pytest.mark.asyncio
-    async def test_opensearch(self):
-        results = await self.wikipya.opensearch(self.SEARCH_QUERY,
-                                                limit=self.SEARCH_LIMIT)
-
-        assert len(results.variants) == self.SEARCH_LIMIT
-
-    @pytest.mark.asyncio
-    async def test_image(self):
-        page = await self.wikipya.page(self.IMAGE_QUERY)
-        image = await page.image(prefix=self.wikipya.prefix)
-
-        if self.CHECK_IMAGE_RES:
-            assert image.width > 0
-            assert image.height > 0
-
-    @pytest.mark.asyncio
-    async def test_getPageName(self):
-        name = await self.wikipya.getPageName(8000432)
-
-        assert name == "Патрик Тёрнер"
-
-    @pytest.mark.asyncio
-    async def test_page(self):
-        page = await self.wikipya.page(self.SEARCH_QUERY)
-
-        for _ in page.text, page.fixed, page.parsed:
-            assert _ != ""
-
-
-class CommonTestsWikipyaLegacy(CommonTestsWikipya):
-    test_getPageName = None
diff --git a/tests/test_fallout.py b/tests/test_fallout.py
deleted file mode 100644
index 7817b40..0000000
--- a/tests/test_fallout.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from test_common import CommonTestsWikipyaLegacy
-
-from wikipya.aiowiki import Wikipya
-from wikipya.clients.mediawiki_legacy import MediaWiki_Legacy
-
-
-class TestFallout(CommonTestsWikipyaLegacy):
-    SEARCH_QUERY = "Марипоза"
-    SEARCH_LIMIT = 1
-    IMAGE_QUERY = "Стрип"
-
-    wikipya = Wikipya(
-        url="https://fallout.fandom.com/ru/api.php",
-        # lurk=True,
-        prefix="",
-        # img_blocklist=blocklist["images"]
-        client=MediaWiki_Legacy
-    )
diff --git a/tests/test_image.py b/tests/test_image.py
new file mode 100644
index 0000000..badc309
--- /dev/null
+++ b/tests/test_image.py
@@ -0,0 +1,12 @@
+import pytest
+from params import Params
+
+
+@pytest.mark.asyncio
+async def test_image(params: Params):
+    wiki = params.client
+    image = await wiki.image(params.image_query)
+
+    if params.check_image_res:
+        assert image.width > 0
+        assert image.height > 0
diff --git a/tests/test_kaiserreich.py b/tests/test_kaiserreich.py
deleted file mode 100644
index 5fd5da6..0000000
--- a/tests/test_kaiserreich.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from test_common import CommonTestsWikipyaLegacy
-
-from wikipya.aiowiki import Wikipya
-from wikipya.clients.mediawiki_legacy import MediaWiki_Legacy
-
-
-class TestKaiserreich(CommonTestsWikipyaLegacy):
-    SEARCH_QUERY = "Германская империя"
-    SEARCH_LIMIT = 1
-
-    wikipya = Wikipya(
-        url="https://kaiserreich.fandom.com/ru/api.php",
-        # lurk=True,
-        prefix="",
-        # img_blocklist=blocklist["images"]
-        client=MediaWiki_Legacy
-    )
diff --git a/tests/test_lurkmore.py b/tests/test_lurkmore.py
deleted file mode 100644
index f9cf29f..0000000
--- a/tests/test_lurkmore.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from test_common import CommonTestsWikipyaLegacy
-
-from wikipya.aiowiki import Wikipya
-from wikipya.clients.mediawiki_lurk import MediaWiki_Lurk
-
-
-class TestLurkmore(CommonTestsWikipyaLegacy):
-    SEARCH_QUERY = "эта страна"
-    SEARCH_LIMIT = 1
-    IMAGE_QUERY = "Украина"
-
-    wikipya = Wikipya(
-        url="https://ipv6.lurkmo.re/api.php",
-        # lurk=True,
-        prefix="",
-        # img_blocklist=blocklist["images"]
-        lurk=True
-    )
diff --git a/tests/test_page.py b/tests/test_page.py
new file mode 100644
index 0000000..7c6b526
--- /dev/null
+++ b/tests/test_page.py
@@ -0,0 +1,20 @@
+import pytest
+from pytest_lazyfixture import lazy_fixture
+
+from params import Params
+
+
+@pytest.mark.parametrize("params", [lazy_fixture("wikipedia")])
+@pytest.mark.asyncio
+async def test_get_page_name(params: Params):
+    wiki = params.client
+    name = await wiki.get_page_name(8000432)
+
+    assert name == "Патрик Тёрнер"
+
+
+@pytest.mark.parametrize("params", [lazy_fixture("wikipedia")])
+@pytest.mark.asyncio
+async def test_summary(params: Params):
+    wiki = params.client
+    await wiki.summary("Патрик Тёрнер")
diff --git a/tests/test_search.py b/tests/test_search.py
new file mode 100644
index 0000000..f3e55e5
--- /dev/null
+++ b/tests/test_search.py
@@ -0,0 +1,18 @@
+import pytest
+from params import Params
+
+
+@pytest.mark.asyncio
+async def test_search(params: Params):
+    wiki = params.client
+
+    results = await wiki.search(params.search_query, limit=params.search_limit)
+    assert len(results) == params.search_limit
+
+
+@pytest.mark.asyncio
+async def test_opensearch(params: Params):
+    wiki = params.client
+
+    results = await wiki.opensearch(params.search_query, params.search_limit)
+    assert len(results.results) == params.search_limit
diff --git a/tests/test_url.py b/tests/test_url.py
new file mode 100644
index 0000000..8906734
--- /dev/null
+++ b/tests/test_url.py
@@ -0,0 +1,9 @@
+from params import Params
+
+
+def test_https(params: Params):
+    # Use HTTPS instead of HTTP, please. Ignore with ALLOW_HTTP
+    wiki = params.client
+
+    is_https_used = wiki.url.base_url.startswith("https://")
+    assert params.allow_http or is_https_used
diff --git a/tests/test_wikipedia.py b/tests/test_wikipedia.py
deleted file mode 100644
index d10f3a9..0000000
--- a/tests/test_wikipedia.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from test_common import CommonTestsWikipya
-
-from wikipya.aiowiki import Wikipya
-
-
-class TestWikipedia(CommonTestsWikipya):
-    wikipya = Wikipya("ru", version="1.35", lurk=False)