diff --git a/action.py b/action.py index 0839873..89fb0dc 100644 --- a/action.py +++ b/action.py @@ -1,135 +1,271 @@ import mwparserfromhell # type: ignore -from typing import Any +from typing import Any, Union from siteinfo import CategoryInfo class Action: """A transformation to a piece of wikitext.""" def apply(self, wikitext: str, category_info: CategoryInfo) -> str: raise NotImplementedError def summary(self, category_info: CategoryInfo) -> str: raise NotImplementedError def is_minor(self) -> bool: """Whether this action, on its own, can be considered a minor edit.""" raise NotImplementedError def cleanup(self) -> None: """Partially normalize the action, as a convenience for users. This should not be used as a replacement for full normalization via the MediaWiki API. """ pass class CategoryAction(Action): """An action to modify a category in the wikitext of a page.""" symbol = '' def __init__(self, category: str): assert category, 'category should not be empty' assert not category.startswith('Category:'), 'category should not include namespace' assert '[' not in category, 'category should not be a wikilink' assert ']' not in category, 'category should not be a wikilink' self.category = category super().__init__() def _is_category(self, wikilink: mwparserfromhell.nodes.wikilink.Wikilink, category_info: CategoryInfo) -> bool: for category_namespace_name in category_info[1]: if wikilink.startswith('[[' + category_namespace_name + ':'): return True return False def _same_category(self, category1: str, category2: str, category_info: CategoryInfo) -> bool: if category_info[2] == 'first-letter': category1 = category1[:1].upper() + category1[1:] category2 = category2[:1].upper() + category2[1:] elif category_info[2] == 'case-sensitive': pass else: raise ValueError('Unknown case handling %s' % category_info[2]) return category1.replace(' ', '_') == category2.replace(' ', '_') def summary(self, category_info: CategoryInfo) -> str: return type(self).symbol + '[[' + category_info[0] + ':' + self.category + ']]' def cleanup(self) -> None: self.category = self.category.replace('_', ' ') def __eq__(self, value: Any) -> bool: return type(self) is type(value) and \ self.category == value.category def __str__(self) -> str: return type(self).symbol + 'Category:' + self.category class AddCategoryAction(CategoryAction): """An action to add a category to the wikitext of a page.""" symbol = '+' def apply(self, wikitext: str, category_info: CategoryInfo) -> str: wikicode = mwparserfromhell.parse(wikitext) last_category = None for wikilink in wikicode.ifilter_wikilinks(): if not self._is_category(wikilink, category_info): continue - if self._same_category(wikilink.title.split(':', 1)[1], self.category, category_info): - return wikitext + if self._accept_category_link(wikilink, category_info): + return str(wikicode) last_category = wikilink - wikilink = mwparserfromhell.nodes.wikilink.Wikilink(category_info[0] + ':' + self.category) + wikilink = self._make_category_link(category_info) if last_category: wikicode.insert_after(last_category, wikilink) wikicode.insert_before(wikilink, '\n') else: if wikicode: wikicode.append('\n') wikicode.append(wikilink) return str(wikicode) + def _accept_category_link(self, wikilink, category_info: CategoryInfo) -> bool: + return self._same_category(wikilink.title.split(':', 1)[1], self.category, category_info) + + def _make_category_link(self, category_info: CategoryInfo): + return mwparserfromhell.nodes.wikilink.Wikilink(category_info[0] + ':' + self.category) + def is_minor(self) -> bool: return True def __repr__(self) -> str: return 'AddCategoryAction(' + repr(self.category) + ')' +class AddCategoryAndSortKeyAction(AddCategoryAction): + """An action to add a category to the wikitext of a page, including a sort key.""" + + sort_key_symbol = '' + + def __init__(self, category: str, sort_key: Union[str, None]): + assert sort_key != '', 'sort key cannot be the empty string' + self.sort_key = sort_key + super().__init__(category) + + def summary(self, category_info: CategoryInfo) -> str: + return '+[[' + category_info[0] + ':' + self.category + '|' + \ + category_info[0] + ':' + self.category + '|' + (self.sort_key or '') + ']]' + + def __eq__(self, value: Any) -> bool: + return type(self) is type(value) and \ + self.category == value.category and \ + self.sort_key == value.sort_key + + def __str__(self) -> str: + return super().__str__() + type(self).sort_key_symbol + (self.sort_key or '') + + +class AddCategoryWithSortKeyAction(AddCategoryAndSortKeyAction): + """An action to add a category with a certain sort key to the wikitext of a page. + + If no category link for that category exists yet, it is added with that sort key. + If such a category link exists, with or without any sort key, no change is made.""" + + sort_key_symbol = '#' + + def _make_category_link(self, category_info: CategoryInfo): + if not self.sort_key: + return super()._make_category_link(category_info) + return mwparserfromhell.nodes.wikilink.Wikilink(category_info[0] + ':' + self.category, + self.sort_key) + + def __repr__(self) -> str: + return 'AddCategoryWithSortKeyAction(' + \ + repr(self.category) + ', ' + \ + repr(self.sort_key) + ')' + + +class AddCategoryProvideSortKeyAction(AddCategoryAndSortKeyAction): + """An action to provide a category with a certain sort key in the wikitext of a page. + + If no category link for that category exists yet, it is added with that sort key. + If such a category link exists, but without a sort key, the sort key is added. + If the existing category specifies a different sort key, no change is made.""" + + sort_key_symbol = '##' + + def _accept_category_link(self, wikilink, category_info: CategoryInfo) -> bool: + if super()._accept_category_link(wikilink, category_info): + if wikilink.text is None: + wikilink.text = self.sort_key + return True + else: + return False + + def _make_category_link(self, category_info: CategoryInfo): + if not self.sort_key: + return super()._make_category_link(category_info) + return mwparserfromhell.nodes.wikilink.Wikilink(category_info[0] + ':' + self.category, + self.sort_key) + + def __repr__(self) -> str: + return 'AddCategoryProvideSortKeyAction(' + \ + repr(self.category) + ', ' + \ + repr(self.sort_key) + ')' + + +class AddCategoryReplaceSortKeyAction(AddCategoryAndSortKeyAction): + """An action to replace a category’s sort key in the wikitext of a page. + + If no category link for that category exists yet, it is added with that sort key. + If such a category link exists, with or without any sort key, its sort key is replaced with the given one.""" + + sort_key_symbol = '###' + + def _accept_category_link(self, wikilink, category_info: CategoryInfo) -> bool: + if super()._accept_category_link(wikilink, category_info): + wikilink.text = self.sort_key + return True + else: + return False + + def _make_category_link(self, category_info: CategoryInfo): + return mwparserfromhell.nodes.wikilink.Wikilink(category_info[0] + ':' + self.category, + self.sort_key) + + def __repr__(self) -> str: + return 'AddCategoryReplaceSortKeyAction(' + \ + repr(self.category) + ', ' + \ + repr(self.sort_key) + ')' + + class RemoveCategoryAction(CategoryAction): """An action to remove a category from the wikitext of a page.""" symbol = '-' def apply(self, wikitext: str, category_info: CategoryInfo) -> str: wikicode = mwparserfromhell.parse(wikitext) for index, wikilink in enumerate(wikicode.nodes): if not isinstance(wikilink, mwparserfromhell.nodes.wikilink.Wikilink): continue if not self._is_category(wikilink, category_info): continue - if self._same_category(wikilink.title.split(':', 1)[1], self.category, category_info): + if self._reject_category_link(wikilink, category_info): # also remove preceding line break if index-1 >= 0 and \ isinstance(wikicode.nodes[index-1], mwparserfromhell.nodes.text.Text) and \ wikicode.nodes[index-1].value.endswith('\n'): wikicode.nodes[index-1].value = wikicode.nodes[index-1].value[:-1] # or following line break elif index+1 < len(wikicode.nodes) and \ isinstance(wikicode.nodes[index+1], mwparserfromhell.nodes.text.Text) and \ wikicode.nodes[index+1].value.startswith('\n'): wikicode.nodes[index+1].value = wikicode.nodes[index+1].value[1:] del wikicode.nodes[index] # this should happen *after* the above blocks, otherwise the indices get confusing break return str(wikicode) + def _reject_category_link(self, wikilink, category_info: CategoryInfo) -> bool: + return self._same_category(wikilink.title.split(':', 1)[1], self.category, category_info) + def is_minor(self) -> bool: return False def __repr__(self) -> str: return 'RemoveCategoryAction(' + repr(self.category) + ')' + + +class RemoveCategoryWithSortKeyAction(RemoveCategoryAction): + """An action to remove a category from the wikitext of a page if it matches a certain sort key.""" + + def __init__(self, category: str, sort_key: Union[str, None]): + assert sort_key != '', 'sort key cannot be the empty string' + self.sort_key = sort_key + super().__init__(category) + + def _reject_category_link(self, wikilink, category_info: CategoryInfo) -> bool: + return super()._reject_category_link(wikilink, category_info) and \ + wikilink.text == self.sort_key + + def summary(self, category_info: CategoryInfo) -> str: + return '-[[' + category_info[0] + ':' + self.category + '|' + \ + category_info[0] + ':' + self.category + '|' + (self.sort_key or '') + ']]' + + def __eq__(self, value: Any) -> bool: + return type(self) is type(value) and \ + self.category == value.category and \ + self.sort_key == value.sort_key + + def __str__(self) -> str: + return super().__str__() + '#' + (self.sort_key or '') + + def __repr__(self) -> str: + return 'RemoveCategoryWithSortKeyAction(' + \ + repr(self.category) + ', ' + \ + repr(self.sort_key) + ')' diff --git a/test_action.py b/test_action.py index 27aa0d6..655b951 100644 --- a/test_action.py +++ b/test_action.py @@ -1,163 +1,319 @@ import pytest # type: ignore -from action import AddCategoryAction, RemoveCategoryAction +from action import AddCategoryAction, AddCategoryWithSortKeyAction, AddCategoryProvideSortKeyAction, AddCategoryReplaceSortKeyAction, RemoveCategoryAction, RemoveCategoryWithSortKeyAction addCategory1 = AddCategoryAction('Cat 1') addCategory2 = AddCategoryAction('Cat 2') +addCategoryWithSortKey1 = AddCategoryWithSortKeyAction('Cat 1', 'sort key') +addCategoryProvideSortKey1 = AddCategoryProvideSortKeyAction('Cat 1', 'sort key') +addCategoryReplaceSortKey1 = AddCategoryReplaceSortKeyAction('Cat 1', 'sort key') removeCategory1 = RemoveCategoryAction('Cat 1') +removeCategoryWithSortKey1 = RemoveCategoryWithSortKeyAction('Cat 1', 'sort key') @pytest.mark.parametrize('clazz', [AddCategoryAction, RemoveCategoryAction]) def test_CategoryAction_init_empty(clazz): with pytest.raises(AssertionError): clazz('') @pytest.mark.parametrize('clazz', [AddCategoryAction, RemoveCategoryAction]) def test_CategoryAction_init_wikilink(clazz): with pytest.raises(AssertionError): clazz('[[Category:Cat 1]]') with pytest.raises(AssertionError): clazz('[[other link]]') @pytest.mark.parametrize('clazz', [AddCategoryAction, RemoveCategoryAction]) def test_CategoryAction_init_category_namespace(clazz): with pytest.raises(AssertionError): clazz('Category:Cat 1') @pytest.mark.parametrize('wikitext, expected', [ ('', '[[Category:Test]]'), ('end of article', 'end of article\n[[Category:Test]]'), ('end of article\n[[Category:A]]\n[[Category:B]]', 'end of article\n[[Category:A]]\n[[Category:B]]\n[[Category:Test]]'), ('some wikitext\n[[Category:Here]]\nmore wikitext', 'some wikitext\n[[Category:Here]]\n[[Category:Test]]\nmore wikitext'), ('it is [[Category:Test]] already present', 'it is [[Category:Test]] already present'), ('it is [[Category:test]] already present in lowercase', 'it is [[Category:test]] already present in lowercase'), ('it is [[Category:TEST]] present in different capitalization', 'it is [[Category:TEST]]\n[[Category:Test]] present in different capitalization'), ('[[Kategorie:Test]]', '[[Kategorie:Test]]'), ('[[K:Test]]', '[[K:Test]]'), ('[[Category:Test|sort key]]', '[[Category:Test|sort key]]'), ('[[:Category:Test]]', '[[:Category:Test]]\n[[Category:Test]]'), ('[[:Category:Test|link text]]', '[[:Category:Test|link text]]\n[[Category:Test]]'), ('[[Category:Test]]', '[[Category:Test]]\n[[Category:Test]]'), ('[[Test]]', '[[Test]]\n[[Category:Test]]'), ('[[Special:Test]]', '[[Special:Test]]\n[[Category:Test]]'), ]) def test_AddCategoryAction_apply(wikitext, expected): action = AddCategoryAction('Test') actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) assert expected == actual def test_AddCategoryAction_apply_detects_underscores(): action = AddCategoryAction('My Test Category') wikitext = '[[Category:My_Test_Category]]' assert wikitext == action.apply(wikitext, ('Category', ['Category'], 'first-letter')) # note: the following test is no longer as relevant since we clean up all new batches to never contain underscores def test_AddCategoryAction_apply_preserves_underscores(): action1 = AddCategoryAction('Test Category 1') action2 = AddCategoryAction('Test_Category_2') action3 = AddCategoryAction('Test_Category 3') expected = '[[Category:Test Category 1]]\n[[Category:Test_Category_2]]\n[[Category:Test_Category 3]]' actual = '' for action in [action1, action2, action3]: actual = action.apply(actual, ('Category', ['Category'], 'first-letter')) assert expected == actual def test_AddCategoryAction_apply_case_sensitive(): action = AddCategoryAction('Test') wikitext = '[[Category:test]]' expected = '[[Category:test]]\n[[Category:Test]]' assert expected == action.apply(wikitext, ('Category', ['Category'], 'case-sensitive')) def test_AddCategoryAction_summary(): assert AddCategoryAction('Test').summary(('Kategorie', ['Kategorie', 'Category'])) == '+[[Kategorie:Test]]' def test_AddCategoryAction_is_minor(): assert AddCategoryAction('Test').is_minor() def test_AddCategoryAction_cleanup(): action = AddCategoryAction('User_input_from_URL') action.cleanup() assert action == AddCategoryAction('User input from URL') def test_AddCategoryAction_eq_same(): assert addCategory1 == addCategory1 def test_AddCategoryAction_eq_equal(): assert addCategory1 == AddCategoryAction(addCategory1.category) def test_AddCategoryAction_eq_different_type(): assert addCategory1 != RemoveCategoryAction(addCategory1.category) def test_AddCategoryAction_eq_different_category(): assert addCategory1 != addCategory2 def test_AddCategoryAction_eq_different_category_normalization(): assert AddCategoryAction('Foo Bar') != AddCategoryAction('Foo_Bar') def test_AddCategoryAction_str(): assert str(addCategory1) == '+Category:Cat 1' def test_AddCategoryAction_repr(): assert eval(repr(addCategory1)) == addCategory1 +@pytest.mark.parametrize('clazz', [AddCategoryWithSortKeyAction, AddCategoryProvideSortKeyAction, AddCategoryReplaceSortKeyAction]) +def test_AddCategoryAndSortKeyAction_init_empty_sort_key(clazz): + with pytest.raises(AssertionError): + clazz('Category', '') + +@pytest.mark.parametrize('clazz', [AddCategoryWithSortKeyAction, AddCategoryProvideSortKeyAction, AddCategoryReplaceSortKeyAction]) +def test_AddCategoryAndSortKeyAction_summary(clazz): + assert clazz('Test', 'Sortierschlüssel').summary(('Kategorie', ['Kategorie', 'Category'])) == '+[[Kategorie:Test|Kategorie:Test|Sortierschlüssel]]' + + +@pytest.mark.parametrize('wikitext, expected', [ + ('', '[[Category:Test|sort key]]'), + ('[[Category:Test]]', '[[Category:Test]]'), + ('[[Category:Test|other sort key]]', '[[Category:Test|other sort key]]'), +]) +def test_AddCategoryWithSortKeyAction_apply(wikitext, expected): + action = AddCategoryWithSortKeyAction('Test', 'sort key') + actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) + assert expected == actual + +def test_AddCategoryWithSortKeyAction_eq_same(): + assert addCategoryWithSortKey1 == addCategoryWithSortKey1 + +def test_AddCategoryWithSortKeyAction_eq_equal(): + assert addCategoryWithSortKey1 == AddCategoryWithSortKeyAction(addCategoryWithSortKey1.category, addCategoryWithSortKey1.sort_key) + +def test_AddCategoryWithSortKeyAction_eq_different_type(): + assert addCategoryWithSortKey1 != AddCategoryProvideSortKeyAction(addCategoryWithSortKey1.category, addCategoryWithSortKey1.sort_key) + +def test_AddCategoryWithSortKeyAction_eq_different_category(): + assert addCategoryWithSortKey1 != AddCategoryWithSortKeyAction('Cat 2', addCategoryWithSortKey1.sort_key) + +def test_AddCategoryWithSortKeyAction_eq_different_sort_key(): + assert addCategoryWithSortKey1 != AddCategoryWithSortKeyAction(addCategoryWithSortKey1.category, 'other sort key') + +def test_AddCategoryWithSortKeyAction_str(): + assert str(addCategoryWithSortKey1) == '+Category:Cat 1#sort key' + +def test_AddCategoryWithSortKeyAction_repr(): + assert eval(repr(addCategoryWithSortKey1)) == addCategoryWithSortKey1 + + +@pytest.mark.parametrize('wikitext, expected', [ + ('', '[[Category:Test|sort key]]'), + ('[[Category:Test]]', '[[Category:Test|sort key]]'), + ('[[Category:Test|other sort key]]', '[[Category:Test|other sort key]]'), +]) +def test_AddCategoryProvideSortKeyAction_apply(wikitext, expected): + action = AddCategoryProvideSortKeyAction('Test', 'sort key') + actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) + assert expected == actual + +def test_AddCategoryProvideSortKeyAction_eq_same(): + assert addCategoryProvideSortKey1 == addCategoryProvideSortKey1 + +def test_AddCategoryProvideSortKeyAction_eq_equal(): + assert addCategoryProvideSortKey1 == AddCategoryProvideSortKeyAction(addCategoryProvideSortKey1.category, addCategoryProvideSortKey1.sort_key) + +def test_AddCategoryProvideSortKeyAction_eq_different_type(): + assert addCategoryProvideSortKey1 != AddCategoryWithSortKeyAction(addCategoryProvideSortKey1.category, addCategoryProvideSortKey1.sort_key) + +def test_AddCategoryProvideSortKeyAction_eq_different_category(): + assert addCategoryProvideSortKey1 != AddCategoryProvideSortKeyAction('Cat 2', addCategoryProvideSortKey1.sort_key) + +def test_AddCategoryProvideSortKeyAction_eq_different_sort_key(): + assert addCategoryProvideSortKey1 != AddCategoryProvideSortKeyAction(addCategoryProvideSortKey1.category, 'other sort key') + +def test_AddCategoryProvideSortKeyAction_str(): + assert str(addCategoryProvideSortKey1) == '+Category:Cat 1##sort key' + +def test_AddCategoryProvideSortKeyAction_repr(): + assert eval(repr(addCategoryProvideSortKey1)) == addCategoryProvideSortKey1 + + +@pytest.mark.parametrize('wikitext, expected', [ + ('', '[[Category:Test|sort key]]'), + ('[[Category:Test]]', '[[Category:Test|sort key]]'), + ('[[Category:Test|other sort key]]', '[[Category:Test|sort key]]'), +]) +def test_AddCategoryReplaceSortKeyAction_apply(wikitext, expected): + action = AddCategoryReplaceSortKeyAction('Test', 'sort key') + actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) + assert expected == actual + +def test_AddCategoryReplaceSortKeyAction_apply_remove_sort_key(): + action = AddCategoryReplaceSortKeyAction('Test', None) + wikitext = '[[Category:Test|sort key]]' + expected = '[[Category:Test]]' + actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) + assert expected == actual + +def test_AddCategoryReplaceSortKeyAction_eq_same(): + assert addCategoryReplaceSortKey1 == addCategoryReplaceSortKey1 + +def test_AddCategoryReplaceSortKeyAction_eq_equal(): + assert addCategoryReplaceSortKey1 == AddCategoryReplaceSortKeyAction(addCategoryReplaceSortKey1.category, addCategoryReplaceSortKey1.sort_key) + +def test_AddCategoryReplaceSortKeyAction_eq_different_type(): + assert addCategoryReplaceSortKey1 != AddCategoryProvideSortKeyAction(addCategoryReplaceSortKey1.category, addCategoryReplaceSortKey1.sort_key) + +def test_AddCategoryReplaceSortKeyAction_eq_different_category(): + assert addCategoryReplaceSortKey1 != AddCategoryReplaceSortKeyAction('Cat 2', addCategoryReplaceSortKey1.sort_key) + +def test_AddCategoryReplaceSortKeyAction_eq_different_sort_key(): + assert addCategoryReplaceSortKey1 != AddCategoryReplaceSortKeyAction(addCategoryReplaceSortKey1.category, 'other sort key') + +def test_AddCategoryReplaceSortKeyAction_str(): + assert str(addCategoryReplaceSortKey1) == '+Category:Cat 1###sort key' + +def test_AddCategoryReplaceSortKeyAction_repr(): + assert eval(repr(addCategoryReplaceSortKey1)) == addCategoryReplaceSortKey1 + + @pytest.mark.parametrize('wikitext, expected', [ ('', ''), ('[[Category:Test]]', ''), ('end of article\n[[Category:Test]]', 'end of article'), ('[[Category:Test]]\nbeginning of article', 'beginning of article'), ('[[Category:Start]][[Category:Test]][[Category:End]]', '[[Category:Start]][[Category:End]]'), ('[[Category:Start]]\n[[Category:Test]]\n[[Category:End]]', '[[Category:Start]]\n[[Category:End]]'), ('[[Category:test]]', ''), ('[[Kategorie:Test]]', ''), ('[[K:Test]]', ''), ('[[Category:Test|sort key]]', ''), ('[[:Category:Test]]', '[[:Category:Test]]'), ('[[:Category:Test|link text]]', '[[:Category:Test|link text]]'), ('[[Category:Test]]', '[[Category:Test]]'), ('[[Test]]', '[[Test]]'), ('[[Special:Test]]', '[[Special:Test]]'), ]) def test_RemoveCategoryAction_apply(wikitext, expected): action = RemoveCategoryAction('Test') actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) assert expected == actual def test_RemoveCategoryAction_apply_case_sensitive(): action = RemoveCategoryAction('Test') wikitext = '[[category:test]]' assert wikitext == action.apply(wikitext, ('Category', ['Category'], 'case-sensitive')) def test_RemoveCategoryAction_summary(): assert RemoveCategoryAction('Test').summary(('Kategorie', ['Kategorie', 'Category'])) == '-[[Kategorie:Test]]' def test_RemoveCategoryAction_is_minor(): assert not RemoveCategoryAction('Test').is_minor() def test_RemoveCategoryAction_cleanup(): action = RemoveCategoryAction('User_input_from_URL') action.cleanup() assert action == RemoveCategoryAction('User input from URL') def test_RemoveCategoryAction_eq_same(): assert removeCategory1 == removeCategory1 def test_RemoveCategoryAction_eq_equal(): assert removeCategory1 == RemoveCategoryAction(removeCategory1.category) def test_RemoveCategoryAction_eq_different_type(): assert removeCategory1 != AddCategoryAction(removeCategory1.category) def test_RemoveCategoryAction_eq_different_category(): assert removeCategory1 != RemoveCategoryAction('Cat 2') def test_RemoveCategoryAction_eq_different_category_normalization(): assert RemoveCategoryAction('Foo Bar') != RemoveCategoryAction('Foo_Bar') def test_RemoveCategoryAction_str(): assert str(removeCategory1) == '-Category:Cat 1' def test_RemoveCategoryAction_repr(): assert eval(repr(removeCategory1)) == removeCategory1 + + +def test_RemoveCategoryWithSortKeyAction_init_empty_sort_key(): + with pytest.raises(AssertionError): + RemoveCategoryWithSortKeyAction('Category', '') + +def test_RemoveCategoryWithSortKeyAction_summary(): + assert RemoveCategoryWithSortKeyAction('Test', 'Sortierschlüssel').summary(('Kategorie', ['Kategorie', 'Category'])) == '-[[Kategorie:Test|Kategorie:Test|Sortierschlüssel]]' + +@pytest.mark.parametrize('wikitext, expected', [ + ('', ''), + ('[[Category:Test]]', '[[Category:Test]]'), + ('[[Category:Test|sort key]]', ''), +]) +def test_RemoveCategoryWithSortKeyAction_apply(wikitext, expected): + action = RemoveCategoryWithSortKeyAction('Test', 'sort key') + actual = action.apply(wikitext, ('Category', ['Category', 'Kategorie', 'K'], 'first-letter')) + assert expected == actual + +def test_RemoveCategoryWithSortKeyAction_eq_same(): + assert removeCategoryWithSortKey1 == removeCategoryWithSortKey1 + +def test_RemoveCategoryWithSortKeyAction_eq_equal(): + assert removeCategoryWithSortKey1 == RemoveCategoryWithSortKeyAction(removeCategoryWithSortKey1.category, removeCategoryWithSortKey1.sort_key) + +def test_RemoveCategoryWithSortKeyAction_eq_different_type(): + assert removeCategoryWithSortKey1 != AddCategoryWithSortKeyAction(removeCategoryWithSortKey1.category, removeCategoryWithSortKey1.sort_key) + +def test_RemoveCategoryWithSortKeyAction_eq_different_category(): + assert removeCategoryWithSortKey1 != RemoveCategoryWithSortKeyAction('Cat 2', removeCategoryWithSortKey1.sort_key) + +def test_RemoveCategoryWithSortKeyAction_eq_different_sort_key(): + assert removeCategoryWithSortKey1 != RemoveCategoryWithSortKeyAction(removeCategoryWithSortKey1.category, 'other sort key') + +def test_RemoveCategoryWithSortKeyAction_str(): + assert str(removeCategoryWithSortKey1) == '-Category:Cat 1#sort key' + +def test_RemoveCategoryWithSortKeyAction_repr(): + assert eval(repr(removeCategoryWithSortKey1)) == removeCategoryWithSortKey1