/*
* Copyright (C) 2023-2024 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 .
*/
import {test as base, chromium} from '@playwright/test';
import path from 'path';
import {fileURLToPath} from 'url';
const dirname = path.dirname(fileURLToPath(import.meta.url));
export const root = path.join(dirname, '..', '..');
export const test = base.extend({
// eslint-disable-next-line no-empty-pattern
context: async ({}, use) => {
const pathToExtension = path.join(root, 'ext');
const context = await chromium.launchPersistentContext('', {
// Disabled: 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;
/**
* @returns {Map}
*/
export function getMockModelFields() {
return new Map([
['Word', '{expression}'],
['Reading', '{furigana-plain}'],
['Sentence', '{clipboard-text}'],
['Audio', '{audio}']
]);
}
/**
* @param {import('playwright').Route} route
* @returns {Promise}
*/
export async function mockAnkiRouteHandler(route) {
try {
/** @type {unknown} */
const requestJson = route.request().postDataJSON();
if (typeof requestJson !== 'object' || requestJson === null) {
throw new Error(`Invalid request type: ${typeof requestJson}`);
}
const body = getResponseBody(/** @type {import('core').SerializableObject} */ (requestJson).action);
const responseJson = {
status: 200,
contentType: 'text/json',
body: JSON.stringify(body)
};
await route.fulfill(responseJson);
} catch {
return await route.abort();
}
}
/**
* @param {import('playwright').Page} page
* @param {string} text
* @returns {Promise}
*/
export const writeToClipboardFromPage = async (page, text) => {
await page.evaluate(`navigator.clipboard.writeText('${text}')`);
};
/**
* @returns {Record}
*/
export function getExpectedAddNoteBody() {
return {
version: 2,
action: 'addNote',
params: {
note: {
fields: {
Word: '読む',
Reading: '読[よ]む',
Audio: '[sound:mock_audio.mp3]',
Sentence: '読むの例文'
},
tags: ['yomitan'],
deckName: 'Mock Deck',
modelName: 'Mock Model',
options: {
allowDuplicate: true,
duplicateScope: 'collection',
duplicateScopeOptions: {
deckName: null,
checkChildren: false,
checkAllModels: false
}
}
}
}
};
}
/**
* @param {unknown} action
* @returns {unknown}
* @throws {Error}
*/
function getResponseBody(action) {
switch (action) {
case 'version': return 6;
case 'deckNames': return ['Mock Deck'];
case 'modelNames': return ['Mock Model'];
case 'modelFieldNames': return [...getMockModelFields().keys()];
case 'canAddNotes': return [true, true];
case 'storeMediaFile': return 'mock_audio.mp3';
case 'addNote': return 102312488912;
default: throw new Error(`Unknown action: ${action}`);
}
}