forked from FooSoft/yomichan
-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #299 from jbukl/pw-search-anki
search.html clipboard monitor, Anki add note Playwright tests
- Loading branch information
Showing
10 changed files
with
309 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -461,6 +461,8 @@ | |
}, | ||
{ | ||
"files": [ | ||
"integration.spec.js", | ||
"playwright-util.js", | ||
"visual.spec.js" | ||
], | ||
"env": { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright (C) 2023 Yomitan Authors | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
const {test: setup} = require('@playwright/test'); | ||
const {ManifestUtil} = require('../../dev/manifest-util'); | ||
const {root} = require('./playwright-util'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
|
||
const manifestPath = path.join(root, 'ext/manifest.json'); | ||
const copyManifestPath = path.join(root, 'ext/manifest-old.json'); | ||
|
||
setup('use test manifest', () => { | ||
const manifestUtil = new ManifestUtil(); | ||
const variant = manifestUtil.getManifest('chrome-playwright'); | ||
fs.renameSync(manifestPath, copyManifestPath); | ||
fs.writeFileSync(manifestPath, ManifestUtil.createManifestString(variant).replace('$YOMITAN_VERSION', '0.0.0.0')); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* | ||
* Copyright (C) 2023 Yomitan Authors | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
const {test: teardown} = require('@playwright/test'); | ||
const {root} = require('./playwright-util'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
|
||
const manifestPath = path.join(root, 'ext/manifest.json'); | ||
const copyManifestPath = path.join(root, 'ext/manifest-old.json'); | ||
|
||
teardown('bring back original manifest', () => { | ||
fs.renameSync(copyManifestPath, manifestPath); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/* | ||
* Copyright (C) 2023 Yomitan Authors | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
const path = require('path'); | ||
const { | ||
test, | ||
expect, | ||
root, | ||
mockModelFieldNames, | ||
mockModelFieldsToAnkiValues, | ||
expectedAddNoteBody, | ||
mockAnkiRouteHandler, | ||
writeToClipboardFromPage | ||
} = require('./playwright-util'); | ||
const {createDictionaryArchive} = require('../../dev/util'); | ||
|
||
test.beforeEach(async ({context}) => { | ||
// wait for the on-install welcome.html tab to load, which becomes the foreground tab | ||
const welcome = await context.waitForEvent('page'); | ||
welcome.close(); // close the welcome tab so our main tab becomes the foreground tab -- otherwise, the screenshot can hang | ||
}); | ||
|
||
test('search clipboard', async ({page, extensionId}) => { | ||
await page.goto(`chrome-extension://${extensionId}/search.html`); | ||
await page.locator('#search-option-clipboard-monitor-container > label').click(); | ||
await page.waitForTimeout(200); // race | ||
|
||
await writeToClipboardFromPage(page, 'あ'); | ||
await expect(page.locator('#search-textbox')).toHaveValue('あ'); | ||
}); | ||
|
||
test('anki add', async ({context, page, extensionId}) => { | ||
// mock anki routes | ||
let resolve; | ||
const addNotePromise = new Promise((res) => { | ||
resolve = res; | ||
}); | ||
await context.route(/127.0.0.1:8765\/*/, (route) => { | ||
mockAnkiRouteHandler(route); | ||
const req = route.request(); | ||
if (req.url().includes('127.0.0.1:8765') && req.postDataJSON().action === 'addNote') { | ||
resolve(req.postDataJSON()); | ||
} | ||
}); | ||
|
||
// open settings | ||
await page.goto(`chrome-extension://${extensionId}/settings.html`); | ||
|
||
// load in test dictionary | ||
const dictionary = createDictionaryArchive(path.join(root, 'test/data/dictionaries/valid-dictionary1'), 'valid-dictionary1'); | ||
const testDictionarySource = await dictionary.generateAsync({type: 'arraybuffer'}); | ||
await page.locator('input[id="dictionary-import-file-input"]').setInputFiles({name: 'valid-dictionary1.zip', buffer: Buffer.from(testDictionarySource)}); | ||
await expect(page.locator('id=dictionaries')).toHaveText('Dictionaries (1 installed, 1 enabled)', {timeout: 5 * 60 * 1000}); | ||
|
||
// connect to anki | ||
await page.locator('.toggle', {has: page.locator('[data-setting="anki.enable"]')}).click(); | ||
await expect(page.locator('#anki-error-message')).toHaveText('Connected'); | ||
|
||
// prep anki deck | ||
await page.locator('[data-modal-action="show,anki-cards"]').click(); | ||
await page.locator('select.anki-card-deck').selectOption('Mock Deck'); | ||
await page.locator('select.anki-card-model').selectOption('Mock Model'); | ||
for (const modelField of mockModelFieldNames) { | ||
await page.locator(`[data-setting="anki.terms.fields.${modelField}"]`).fill(mockModelFieldsToAnkiValues[modelField]); | ||
} | ||
await page.locator('#anki-cards-modal > div > div.modal-footer > button:nth-child(2)').click(); | ||
await writeToClipboardFromPage(page, '読むの例文'); | ||
|
||
// add to anki deck | ||
await page.goto(`chrome-extension://${extensionId}/search.html`); | ||
await page.waitForTimeout(500); // race | ||
await page.locator('#search-textbox').fill('読む'); | ||
await page.locator('#search-textbox').press('Enter'); | ||
await page.locator('[data-mode="term-kanji"]').click(); | ||
const addNoteReqBody = await addNotePromise; | ||
expect(addNoteReqBody).toMatchObject(expectedAddNoteBody); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* | ||
* Copyright (C) 2023 Yomitan Authors | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
*/ | ||
|
||
const path = require('path'); | ||
const {test: base, chromium} = require('@playwright/test'); | ||
|
||
export const root = path.join(__dirname, '..', '..'); | ||
|
||
export const test = base.extend({ | ||
context: async ({ }, use) => { | ||
const pathToExtension = path.join(root, 'ext'); | ||
const context = await chromium.launchPersistentContext('', { | ||
// headless: false, | ||
args: [ | ||
'--headless=new', | ||
`--disable-extensions-except=${pathToExtension}`, | ||
`--load-extension=${pathToExtension}` | ||
] | ||
}); | ||
await use(context); | ||
await context.close(); | ||
}, | ||
extensionId: async ({context}, use) => { | ||
let [background] = context.serviceWorkers(); | ||
if (!background) { | ||
background = await context.waitForEvent('serviceworker'); | ||
} | ||
|
||
const extensionId = background.url().split('/')[2]; | ||
await use(extensionId); | ||
} | ||
}); | ||
export const expect = test.expect; | ||
|
||
export const mockModelFieldNames = [ | ||
'Word', | ||
'Reading', | ||
'Audio', | ||
'Sentence' | ||
]; | ||
|
||
export const mockModelFieldsToAnkiValues = { | ||
'Word': '{expression}', | ||
'Reading': '{furigana-plain}', | ||
'Sentence': '{clipboard-text}', | ||
'Audio': '{audio}' | ||
}; | ||
|
||
export const mockAnkiRouteHandler = (route) => { | ||
const reqBody = route.request().postDataJSON(); | ||
const respBody = ankiRouteResponses[reqBody.action]; | ||
if (!respBody) { | ||
return route.abort(); | ||
} | ||
route.fulfill(respBody); | ||
}; | ||
|
||
export const writeToClipboardFromPage = async (page, text) => { | ||
await page.evaluate(`navigator.clipboard.writeText('${text}')`); | ||
}; | ||
|
||
export const expectedAddNoteBody = { | ||
'action': 'addNote', | ||
'params': | ||
{ | ||
'note': { | ||
'fields': { | ||
'Word': '読む', 'Reading': '読[よ]む', 'Audio': '[sound:mock_audio.mp3]', 'Sentence': '読むの例文' | ||
}, | ||
'tags': ['yomitan'], | ||
'deckName': 'Mock Deck', | ||
'modelName': 'Mock Model', | ||
'options': { | ||
'allowDuplicate': false, 'duplicateScope': 'collection', 'duplicateScopeOptions': { | ||
'deckName': null, 'checkChildren': false, 'checkAllModels': false | ||
} | ||
} | ||
} | ||
}, 'version': 2 | ||
}; | ||
|
||
const baseAnkiResp = { | ||
status: 200, | ||
contentType: 'text/json' | ||
}; | ||
|
||
const ankiRouteResponses = { | ||
'version': Object.assign({body: JSON.stringify(6)}, baseAnkiResp), | ||
'deckNames': Object.assign({body: JSON.stringify(['Mock Deck'])}, baseAnkiResp), | ||
'modelNames': Object.assign({body: JSON.stringify(['Mock Model'])}, baseAnkiResp), | ||
'modelFieldNames': Object.assign({body: JSON.stringify(mockModelFieldNames)}, baseAnkiResp), | ||
'canAddNotes': Object.assign({body: JSON.stringify([true, true])}, baseAnkiResp), | ||
'storeMediaFile': Object.assign({body: JSON.stringify('mock_audio.mp3')}, baseAnkiResp), | ||
'addNote': Object.assign({body: JSON.stringify(102312488912)}, baseAnkiResp) | ||
}; |
Oops, something went wrong.