Merge branch 'master' of https://github.com/FooSoft/anki-connect
This commit is contained in:
commit
4cef534271
1730
AnkiConnect.py
1730
AnkiConnect.py
File diff suppressed because it is too large
Load Diff
3
test.sh
Executable file
3
test.sh
Executable file
@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/sh
|
||||||
|
|
||||||
|
python -m unittest discover -v tests
|
61
tests/test_cards.py
Executable file
61
tests/test_cards.py
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class TestCards(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
util.invoke('createDeck', deck='test')
|
||||||
|
note = {'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front1', 'Back': 'back1'}, 'tags': ['tag1']}
|
||||||
|
self.noteId = util.invoke('addNote', note=note)
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
util.invoke('deleteDecks', decks=['test'], cardsToo=True)
|
||||||
|
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
# findCards
|
||||||
|
cardIds = util.invoke('findCards', query='deck:test')
|
||||||
|
self.assertEqual(len(cardIds), 1)
|
||||||
|
|
||||||
|
# suspend
|
||||||
|
util.invoke('suspend', cards=cardIds)
|
||||||
|
|
||||||
|
# areSuspended (part 1)
|
||||||
|
suspendedStates = util.invoke('areSuspended', cards=cardIds)
|
||||||
|
self.assertEqual(len(cardIds), len(suspendedStates))
|
||||||
|
self.assertNotIn(False, suspendedStates)
|
||||||
|
|
||||||
|
# unsuspend
|
||||||
|
util.invoke('unsuspend', cards=cardIds)
|
||||||
|
|
||||||
|
# areSuspended (part 2)
|
||||||
|
suspendedStates = util.invoke('areSuspended', cards=cardIds)
|
||||||
|
self.assertEqual(len(cardIds), len(suspendedStates))
|
||||||
|
self.assertNotIn(True, suspendedStates)
|
||||||
|
|
||||||
|
# areDue
|
||||||
|
dueStates = util.invoke('areDue', cards=cardIds)
|
||||||
|
self.assertEqual(len(cardIds), len(dueStates))
|
||||||
|
self.assertNotIn(False, dueStates)
|
||||||
|
|
||||||
|
# getIntervals
|
||||||
|
util.invoke('getIntervals', cards=cardIds, complete=True)
|
||||||
|
util.invoke('getIntervals', cards=cardIds, complete=False)
|
||||||
|
|
||||||
|
# cardsToNotes
|
||||||
|
noteIds = util.invoke('cardsToNotes', cards=cardIds)
|
||||||
|
self.assertEqual(len(noteIds), len(cardIds))
|
||||||
|
self.assertIn(self.noteId, noteIds)
|
||||||
|
|
||||||
|
# cardsInfo
|
||||||
|
cardsInfo = util.invoke('cardsInfo', cards=cardIds)
|
||||||
|
self.assertEqual(len(cardsInfo), len(cardIds))
|
||||||
|
for i, cardInfo in enumerate(cardsInfo):
|
||||||
|
self.assertEqual(cardInfo['cardId'], cardIds[i])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
77
tests/test_decks.py
Normal file → Executable file
77
tests/test_decks.py
Normal file → Executable file
@ -1,16 +1,71 @@
|
|||||||
# -*- coding: utf-8 -*-
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import TestCase
|
import util
|
||||||
from util import callAnkiConnectEndpoint
|
|
||||||
|
|
||||||
class TestDeckNames(TestCase):
|
|
||||||
|
|
||||||
def test_deckNames(self):
|
class TestDecks(unittest.TestCase):
|
||||||
response = callAnkiConnectEndpoint({'action': 'deckNames'})
|
def runTest(self):
|
||||||
self.assertEqual(['Default'], response)
|
# deckNames (part 1)
|
||||||
|
deckNames = util.invoke('deckNames')
|
||||||
|
self.assertIn('Default', deckNames)
|
||||||
|
|
||||||
class TestGetDeckConfig(TestCase):
|
# deckNamesAndIds
|
||||||
|
result = util.invoke('deckNamesAndIds')
|
||||||
|
self.assertIn('Default', result)
|
||||||
|
self.assertEqual(result['Default'], 1)
|
||||||
|
|
||||||
def test_getDeckConfig(self):
|
# createDeck
|
||||||
response = callAnkiConnectEndpoint({'action': 'getDeckConfig', 'params': {'deck': 'Default'}})
|
util.invoke('createDeck', deck='test1')
|
||||||
self.assertDictContainsSubset({'name': 'Default', 'replayq': True}, response)
|
|
||||||
|
# deckNames (part 2)
|
||||||
|
deckNames = util.invoke('deckNames')
|
||||||
|
self.assertIn('test1', deckNames)
|
||||||
|
|
||||||
|
# changeDeck
|
||||||
|
note = {'deckName': 'test1', 'modelName': 'Basic', 'fields': {'Front': 'front', 'Back': 'back'}, 'tags': ['tag']}
|
||||||
|
noteId = util.invoke('addNote', note=note)
|
||||||
|
cardIds = util.invoke('findCards', query='deck:test1')
|
||||||
|
util.invoke('changeDeck', cards=cardIds, deck='test2')
|
||||||
|
|
||||||
|
# deckNames (part 3)
|
||||||
|
deckNames = util.invoke('deckNames')
|
||||||
|
self.assertIn('test2', deckNames)
|
||||||
|
|
||||||
|
# deleteDecks
|
||||||
|
util.invoke('deleteDecks', decks=['test1', 'test2'], cardsToo=True)
|
||||||
|
|
||||||
|
# deckNames (part 4)
|
||||||
|
deckNames = util.invoke('deckNames')
|
||||||
|
self.assertNotIn('test1', deckNames)
|
||||||
|
self.assertNotIn('test2', deckNames)
|
||||||
|
|
||||||
|
# getDeckConfig
|
||||||
|
deckConfig = util.invoke('getDeckConfig', deck='Default')
|
||||||
|
self.assertEqual('Default', deckConfig['name'])
|
||||||
|
|
||||||
|
# saveDeckConfig
|
||||||
|
deckConfig = util.invoke('saveDeckConfig', config=deckConfig)
|
||||||
|
|
||||||
|
# setDeckConfigId
|
||||||
|
setDeckConfigId = util.invoke('setDeckConfigId', decks=['Default'], configId=1)
|
||||||
|
self.assertTrue(setDeckConfigId)
|
||||||
|
|
||||||
|
# cloneDeckConfigId (part 1)
|
||||||
|
deckConfigId = util.invoke('cloneDeckConfigId', cloneFrom=1, name='test')
|
||||||
|
self.assertTrue(deckConfigId)
|
||||||
|
|
||||||
|
# removeDeckConfigId (part 1)
|
||||||
|
removedDeckConfigId = util.invoke('removeDeckConfigId', configId=deckConfigId)
|
||||||
|
self.assertTrue(removedDeckConfigId)
|
||||||
|
|
||||||
|
# removeDeckConfigId (part 2)
|
||||||
|
removedDeckConfigId = util.invoke('removeDeckConfigId', configId=deckConfigId)
|
||||||
|
self.assertFalse(removedDeckConfigId)
|
||||||
|
|
||||||
|
# cloneDeckConfigId (part 2)
|
||||||
|
deckConfigId = util.invoke('cloneDeckConfigId', cloneFrom=deckConfigId, name='test')
|
||||||
|
self.assertFalse(deckConfigId)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
41
tests/test_graphical.py
Executable file
41
tests/test_graphical.py
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class TestGui(unittest.TestCase):
|
||||||
|
def runTest(self):
|
||||||
|
# guiBrowse
|
||||||
|
util.invoke('guiBrowse', query='deck:Default')
|
||||||
|
|
||||||
|
# guiAddCards
|
||||||
|
util.invoke('guiAddCards')
|
||||||
|
|
||||||
|
# guiCurrentCard
|
||||||
|
# util.invoke('guiCurrentCard')
|
||||||
|
|
||||||
|
# guiStartCardTimer
|
||||||
|
util.invoke('guiStartCardTimer')
|
||||||
|
|
||||||
|
# guiShowQuestion
|
||||||
|
util.invoke('guiShowQuestion')
|
||||||
|
|
||||||
|
# guiShowAnswer
|
||||||
|
util.invoke('guiShowAnswer')
|
||||||
|
|
||||||
|
# guiAnswerCard
|
||||||
|
util.invoke('guiAnswerCard', ease=1)
|
||||||
|
|
||||||
|
# guiDeckOverview
|
||||||
|
util.invoke('guiDeckOverview', name='Default')
|
||||||
|
|
||||||
|
# guiDeckBrowser
|
||||||
|
util.invoke('guiDeckBrowser')
|
||||||
|
|
||||||
|
# guiExitAnki
|
||||||
|
# util.invoke('guiExitAnki')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
28
tests/test_media.py
Executable file
28
tests/test_media.py
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class TestMedia(unittest.TestCase):
|
||||||
|
def runTest(self):
|
||||||
|
filename = '_test.txt'
|
||||||
|
data = 'test'
|
||||||
|
|
||||||
|
# storeMediaFile
|
||||||
|
util.invoke('storeMediaFile', filename='_test.txt', data=data)
|
||||||
|
|
||||||
|
# retrieveMediaFile (part 1)
|
||||||
|
media = util.invoke('retrieveMediaFile', filename=filename)
|
||||||
|
self.assertEqual(media, data)
|
||||||
|
|
||||||
|
# deleteMediaFile
|
||||||
|
util.invoke('deleteMediaFile', filename=filename)
|
||||||
|
|
||||||
|
# retrieveMediaFile (part 2)
|
||||||
|
media = util.invoke('retrieveMediaFile', filename=filename)
|
||||||
|
self.assertFalse(media)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
32
tests/test_misc.py
Normal file → Executable file
32
tests/test_misc.py
Normal file → Executable file
@ -1,10 +1,28 @@
|
|||||||
# -*- coding: utf-8 -*-
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
from unittest import TestCase
|
import util
|
||||||
from util import callAnkiConnectEndpoint
|
|
||||||
|
|
||||||
class TestVersion(TestCase):
|
|
||||||
|
|
||||||
def test_version(self):
|
class TestMisc(unittest.TestCase):
|
||||||
response = callAnkiConnectEndpoint({'action': 'version'})
|
def runTest(self):
|
||||||
self.assertEqual(5, response)
|
# version
|
||||||
|
self.assertEqual(util.invoke('version'), 5)
|
||||||
|
|
||||||
|
# upgrade
|
||||||
|
util.invoke('upgrade')
|
||||||
|
|
||||||
|
# sync
|
||||||
|
util.invoke('sync')
|
||||||
|
|
||||||
|
# multi
|
||||||
|
actions = [util.request('version'), util.request('version'), util.request('version')]
|
||||||
|
results = util.invoke('multi', actions=actions)
|
||||||
|
self.assertEqual(len(results), len(actions))
|
||||||
|
for result in results:
|
||||||
|
self.assertIsNone(result['error'])
|
||||||
|
self.assertEqual(result['result'], 5)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
25
tests/test_models.py
Executable file
25
tests/test_models.py
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class TestModels(unittest.TestCase):
|
||||||
|
def runTest(self):
|
||||||
|
# modelNames
|
||||||
|
modelNames = util.invoke('modelNames')
|
||||||
|
self.assertGreater(len(modelNames), 0)
|
||||||
|
|
||||||
|
# modelNamesAndIds
|
||||||
|
modelNamesAndIds = util.invoke('modelNamesAndIds')
|
||||||
|
self.assertGreater(len(modelNames), 0)
|
||||||
|
|
||||||
|
# modelFieldNames
|
||||||
|
modelFields = util.invoke('modelFieldNames', modelName=modelNames[0])
|
||||||
|
|
||||||
|
# modelFieldsOnTemplates
|
||||||
|
modelFieldsOnTemplates = util.invoke('modelFieldsOnTemplates', modelName=modelNames[0])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
89
tests/test_notes.py
Executable file
89
tests/test_notes.py
Executable file
@ -0,0 +1,89 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
import util
|
||||||
|
|
||||||
|
|
||||||
|
class TestNotes(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
util.invoke('createDeck', deck='test')
|
||||||
|
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
util.invoke('deleteDecks', decks=['test'], cardsToo=True)
|
||||||
|
|
||||||
|
|
||||||
|
def runTest(self):
|
||||||
|
# addNote
|
||||||
|
note = {'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front1', 'Back': 'back1'}, 'tags': ['tag1']}
|
||||||
|
noteId = util.invoke('addNote', note=note)
|
||||||
|
self.assertRaises(Exception, lambda: util.invoke('addNote', note=note))
|
||||||
|
|
||||||
|
# addTags
|
||||||
|
util.invoke('addTags', notes=[noteId], tags='tag2')
|
||||||
|
|
||||||
|
# notesInfo (part 1)
|
||||||
|
noteInfos = util.invoke('notesInfo', notes=[noteId])
|
||||||
|
self.assertEqual(len(noteInfos), 1)
|
||||||
|
noteInfo = noteInfos[0]
|
||||||
|
self.assertEqual(noteInfo['noteId'], noteId)
|
||||||
|
self.assertSetEqual(set(noteInfo['tags']), {'tag1', 'tag2'})
|
||||||
|
self.assertEqual(noteInfo['fields']['Front']['value'], 'front1')
|
||||||
|
self.assertEqual(noteInfo['fields']['Back']['value'], 'back1')
|
||||||
|
|
||||||
|
# getTags
|
||||||
|
allTags = util.invoke('getTags')
|
||||||
|
self.assertIn('tag1', allTags)
|
||||||
|
self.assertIn('tag2', allTags)
|
||||||
|
|
||||||
|
# removeTags
|
||||||
|
util.invoke('removeTags', notes=[noteId], tags='tag2')
|
||||||
|
|
||||||
|
# updateNoteFields
|
||||||
|
noteUpdate = {'id': noteId, 'fields': {'Front': 'front2', 'Back': 'back2'}}
|
||||||
|
util.invoke('updateNoteFields', note=noteUpdate)
|
||||||
|
|
||||||
|
# notesInfo (part 2)
|
||||||
|
noteInfos = util.invoke('notesInfo', notes=[noteId])
|
||||||
|
self.assertEqual(len(noteInfos), 1)
|
||||||
|
noteInfo = noteInfos[0]
|
||||||
|
self.assertSetEqual(set(noteInfo['tags']), {'tag1'})
|
||||||
|
self.assertIn('tag1', noteInfo['tags'])
|
||||||
|
self.assertNotIn('tag2', noteInfo['tags'])
|
||||||
|
self.assertEqual(noteInfo['fields']['Front']['value'], 'front2')
|
||||||
|
self.assertEqual(noteInfo['fields']['Back']['value'], 'back2')
|
||||||
|
|
||||||
|
notes = [
|
||||||
|
{'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front3', 'Back': 'back3'}, 'tags': ['tag']},
|
||||||
|
{'deckName': 'test', 'modelName': 'Basic', 'fields': {'Front': 'front4', 'Back': 'back4'}, 'tags': ['tag']}
|
||||||
|
]
|
||||||
|
|
||||||
|
# canAddNotes (part 1)
|
||||||
|
noteStates = util.invoke('canAddNotes', notes=notes)
|
||||||
|
self.assertEqual(len(noteStates), len(notes))
|
||||||
|
self.assertNotIn(False, noteStates)
|
||||||
|
|
||||||
|
# addNotes (part 1)
|
||||||
|
noteIds = util.invoke('addNotes', notes=notes)
|
||||||
|
self.assertEqual(len(noteIds), len(notes))
|
||||||
|
for noteId in noteIds:
|
||||||
|
self.assertNotEqual(noteId, None)
|
||||||
|
|
||||||
|
# canAddNotes (part 2)
|
||||||
|
noteStates = util.invoke('canAddNotes', notes=notes)
|
||||||
|
self.assertNotIn(True, noteStates)
|
||||||
|
self.assertEqual(len(noteStates), len(notes))
|
||||||
|
|
||||||
|
# addNotes (part 2)
|
||||||
|
noteIds = util.invoke('addNotes', notes=notes)
|
||||||
|
self.assertEqual(len(noteIds), len(notes))
|
||||||
|
for noteId in noteIds:
|
||||||
|
self.assertEqual(noteId, None)
|
||||||
|
|
||||||
|
# findNotes
|
||||||
|
noteIds = util.invoke('findNotes', query='deck:test')
|
||||||
|
self.assertEqual(len(noteIds), len(notes) + 1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -1,11 +1,23 @@
|
|||||||
import json
|
import json
|
||||||
import urllib
|
|
||||||
import urllib2
|
import urllib2
|
||||||
|
|
||||||
def callAnkiConnectEndpoint(data):
|
API_VERSION = 5
|
||||||
url = 'http://docker:8888'
|
API_URL = 'http://localhost:8765'
|
||||||
dumpedData = json.dumps(data)
|
|
||||||
req = urllib2.Request(url, dumpedData)
|
|
||||||
response = urllib2.urlopen(req).read()
|
def request(action, **params):
|
||||||
responseData = json.loads(response)
|
return {'action': action, 'params': params, 'version': API_VERSION}
|
||||||
return responseData
|
|
||||||
|
|
||||||
|
def invoke(action, **params):
|
||||||
|
requestJson = json.dumps(request(action, **params))
|
||||||
|
response = json.load(urllib2.urlopen(urllib2.Request(API_URL, requestJson)))
|
||||||
|
if len(response) != 2:
|
||||||
|
raise Exception('response has an unexpected number of fields')
|
||||||
|
if 'error' not in response:
|
||||||
|
raise Exception('response is missing required error field')
|
||||||
|
if 'result' not in response:
|
||||||
|
raise Exception('response is missing required result field')
|
||||||
|
if response['error'] is not None:
|
||||||
|
raise Exception(response['error'])
|
||||||
|
return response['result']
|
||||||
|
Loading…
Reference in New Issue
Block a user