test(web): add e2e tests for KMX keyboard with web-core

Also add similar tests for a JS keyboard.

Test-bot: skip
This commit is contained in:
Eberhard Beilharz 2025-11-24 20:02:17 +01:00
parent 7467584364
commit 12d56453a4
No known key found for this signature in database
GPG key ID: E9140597606020D3
9 changed files with 257 additions and 35 deletions

17
package-lock.json generated
View file

@ -2404,6 +2404,22 @@
"node": ">=14"
}
},
"node_modules/@playwright/test": {
"version": "1.46.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.46.1.tgz",
"integrity": "sha512-Fq6SwLujA/DOIvNC2EL/SojJnkKf/rAwJ//APpJJHRyMi1PdKrY3Az+4XNQ51N4RTbItbIByQ0jgd1tayq1aeA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.46.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@puppeteer/browsers": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.0.tgz",
@ -14161,6 +14177,7 @@
},
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"@playwright/test": "^1.46.1",
"@sentry/cli": "^2.31.0",
"@zip.js/zip.js": "^2.7.32",
"c8": "^7.12.0",

6
web/.gitignore vendored
View file

@ -11,4 +11,8 @@ node_modules/
source/environment.inc.ts
**/.idea/**/*.xml
**/*.iml
**/*.kpj.user
**/*.kpj.user
# Playwright
playwright-report/
test-results/

View file

@ -40,6 +40,7 @@ builder_describe "Builds engine modules for Keyman Engine for Web (KMW)." \
\
"--test-dom-only For test, run only DOM-oriented unit tests (reduced footprint, nothing browser-specific)" \
"--test-integrated-only For test, run only KMW's integration test suite" \
"--test-e2e-only For test, run only KMW's end-to-end test suite" \
"--test-inspect For test, run browser-based unit tests in an inspectable mode"
builder_parse "$@"
@ -168,6 +169,24 @@ builder_run_action build:_all build_tests_action
# Run tests
builder_run_child_actions test
function is_test_included() {
local test_opt=$1
local run_dom_tests=true run_integrated_tests=true run_e2e_tests=true
local var_name="run_${test_opt}_tests"
if builder_has_option --test-integrated-only || builder_has_option --test-e2e-only; then
run_dom_tests=false
fi
if builder_has_option --test-dom-only || builder_has_option --test-e2e-only; then
run_integrated_tests=false
fi
if builder_has_option --test-dom-only || builder_has_option --test-integrated-only; then
run_e2e_tests=false
fi
${!var_name}
}
function do_browser_tests() {
# Browser-based tests: common configs & kill-switches
@ -175,6 +194,7 @@ function do_browser_tests() {
local WTR_CONFIG=
if builder_is_ci_build; then
WTR_CONFIG=.CI
export KEYMAN_IS_CI_BUILD=1
fi
# Prepare the flags for the karma command.
@ -184,12 +204,15 @@ function do_browser_tests() {
fi
pushd "${KEYMAN_ROOT}"
if builder_has_option --test-dom-only || ! builder_has_option --test-integrated-only; then
if is_test_included dom; then
web-test-runner --config "web/src/test/auto/dom/web-test-runner${WTR_CONFIG}.config.mjs" ${WTR_INSPECT}
fi
if builder_has_option --test-integrated-only || ! builder_has_option --test-dom-only; then
if is_test_included integrated; then
web-test-runner --config "web/src/test/auto/integrated/web-test-runner${WTR_CONFIG}.config.mjs" ${WTR_INSPECT}
fi
if is_test_included e2e; then
npx playwright test --config "web/src/test/auto/e2e/playwright.config.ts"
fi
popd
}

View file

@ -104,6 +104,7 @@
"homepage": "https://github.com/keymanapp/keyman#readme",
"devDependencies": {
"@keymanapp/resources-gosh": "*",
"@playwright/test": "^1.46.1",
"@sentry/cli": "^2.31.0",
"@zip.js/zip.js": "^2.7.32",
"c8": "^7.12.0",
@ -116,9 +117,9 @@
"test": "gosh ./test.sh"
},
"dependencies": {
"@keymanapp/common-types": "*",
"@keymanapp/keyman-version": "*",
"@keymanapp/lexical-model-layer": "*",
"@keymanapp/common-types": "*",
"@keymanapp/recorder-core": "*",
"core-js": "^3.34.0",
"eventemitter3": "^5.0.0",

View file

@ -0,0 +1,18 @@
# Keyman Engine for Web e2e tests
These tests are run as part of the regular build, but can be run separately
with:
```bash
web/test.sh test:e2e
```
The Playwright Test for VSCode extension (ms-playwright.playwright) allows to run
the tests in VSCode.
It's also possible to run them with a UI that shows screenshots
captured while running the tests:
```bash
npx playwright test --config "web/src/test/auto/e2e/playwright.config.ts" --ui
```

View file

@ -0,0 +1,66 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*/
import { test, expect, Page, Locator } from '@playwright/test';
test.describe('KMX keyboards', function () {
let textarea: Locator;
const beforeEach = async (page: Page) => {
await page.goto('http://localhost:3000/src/test/manual/web/kmxkeyboard.html?keyboard=kmx');
await page.evaluate(async () => { await window.KmwLoaded; });
textarea = page.locator('#inputarea');
await textarea.click();
// we don't have a OSK yet, so don't wait for it to appear
}
test('can type on a (simulated) hardware keyboard', async ({ page }) => {
await beforeEach(page);
await page.keyboard.press('x');
await page.keyboard.press('j');
await page.keyboard.press('m');
await page.keyboard.press('Shift+e');
await page.keyboard.press('r');
await expect(textarea).toHaveValue('ខ្មែរ', { timeout: 500 });
});
test('can type on a (simulated) hardware keyboard with reordering', async ({ page }) => {
await beforeEach(page);
await page.keyboard.press('x');
await page.keyboard.press('Shift+e');
await page.keyboard.press('j');
await page.keyboard.press('m');
await page.keyboard.press('r');
await expect(textarea).toHaveValue('ខ្មែរ', { timeout: 500 });
});
});
test.describe('JS keyboards', function () {
let textarea: Locator;
const beforeEach = async (page: Page) => {
await page.goto('http://localhost:3000/src/test/manual/web/kmxkeyboard.html?keyboard=js');
await page.evaluate(async () => { await window.KmwLoaded; });
textarea = page.locator('#inputarea');
await textarea.click();
await page.evaluate(async () => { await window.OskShown; });
};
test('can type on a (simulated) hardware keyboard', async ({ page }) => {
await beforeEach(page);
await page.keyboard.press('x');
await page.keyboard.press('j');
await page.keyboard.press('m');
await page.keyboard.press('Shift+e');
await page.keyboard.press('r');
await expect(textarea).toHaveValue('ខ្មែរ', { timeout: 500 });
});
test('can type on a (simulated) hardware keyboard with reordering', async ({ page }) => {
await beforeEach(page);
await page.keyboard.press('x');
await page.keyboard.press('Shift+e');
await page.keyboard.press('j');
await page.keyboard.press('m');
await page.keyboard.press('r');
await expect(textarea).toHaveValue('ខ្មែរ', { timeout: 500 });
});
});

View file

@ -0,0 +1,84 @@
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig, devices } from '@playwright/test';
const dir = dirname(fileURLToPath(import.meta.url));
const KEYMAN_ROOT = resolve(dir, '../../../../../');
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
rootDir: KEYMAN_ROOT,
testDir: '.',
testMatch: '*.tests.ts',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.KEYMAN_IS_CI_BUILD,
/* Retry on CI only */
retries: process.env.KEYMAN_IS_CI_BUILD ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.KEYMAN_IS_CI_BUILD ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
// reporter: process.env.KEYMAN_IS_CI_BUILD ? [
// [KEYMAN_ROOT + '/common/test/resources/playwright-TC-reporter.ts', { parentFlow: 'web_tests' }]
// ] : [
reporter: [
['list'],
['html', { open: 'never' }],
],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
webServer: {
command: `${KEYMAN_ROOT}/web/build.sh start`,
url: 'http://localhost:3000',
reuseExistingServer: !process.env.KEYMAN_IS_CI_BUILD,
},
});

View file

@ -12,7 +12,7 @@
<!-- Enable IE9 Standards mode -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>KeymanWeb Sample Page - Unminified Source</title>
<title>KeymanWeb Khmer KMX/JS test page</title>
<!-- Your page CSS -->
<style type='text/css'>
@ -36,19 +36,35 @@
<!-- Initialization: set paths to keyboards, resources and fonts as required -->
<script>
window.addEventListener('load', function () {
keyman.init().then(function () {
keyman.addKeyboards({
const searchParams = new URLSearchParams(window.location.search);
const kmx = {
id: 'khmer_angkor',
name: 'Khmer Angkor',
languages: {
id: 'km',
name: 'Khmer'
},
filename: '/build/test-resources/keyboards/khmer_angkor.kmx'
languages: { id: 'km', name: 'Khmer' }, filename: '/build/test-resources/keyboards/khmer_angkor.kmx' };
let keyboard;
switch (searchParams.get('keyboard')) {
case 'js':
keyboard = { id: 'khmer_angkor', name: 'Khmer Angkor', languages: { id: 'km' } };
break;
case 'kmx':
default:
keyboard = kmx;
break;
};
// allow to wait on KMW being fully initialized
window.KmwLoaded = new Promise((resolve) => {
window.addEventListener('load', async function () {
await keyman.init();
await keyman.addKeyboards(keyboard);
// allow to wait on OSK being visible
window.OskShown = new Promise((resolve) => {
keyman.osk.addEventListener('show', async function () {
resolve(true);
});
});
resolve(true);
});
});
});
</script>
<!-- Add keyboard management script for local selection of keyboards to use -->
@ -58,7 +74,7 @@
<!-- Sample page HTML -->
<body>
<h2>KeymanWeb Sample Page - KMX Keyboards</h2>
<h2>KeymanWeb Sample Page - Khmer Angkor KMX and JS Keyboards</h2>
<div>
<!--
@ -68,24 +84,13 @@
-->
<div id='KeymanWebControl'></div>
<h3>Type in your language in this text area:</h3>
<textarea id='ta1' class='test' placeholder='Type here'></textarea>
<h3>or in this input field:</h3>
<input class='test' value='' placeholder='or here'/>
<!-- The following elements show how the language menu can be dynamically extended at any time -->
<h3>Add a keyboard by keyboard name:</h3>
<input type='input' id='kbd_id1' class='kmw-disabled' onkeypress="clickOnEnter(event,1);"/>
<input type='button' id='btn1' onclick='addKeyboard(1);' value='Add' />
<h3>Add a keyboard by BCP-47 language code:</h3>
<input type='input' id='kbd_id2' class='kmw-disabled' onkeypress="clickOnEnter(event,2);"/>
<input type='button' id='btn2' onclick='addKeyboard(2);' value='Add' />
<h3>Add a keyboard by language name(s):</h3>
<input type='input' id='kbd_id3' class='kmw-disabled' onkeypress="clickOnEnter(event,3);"/>
<input type='button' id='btn3' onclick='addKeyboard(3);' value='Add' />
<p>This page allows to use either the .kmx or the .js version of the Khmer Angkor keyboard.
Which one can be selected by the <code>keyboard</code> query parameter:
append <code>?keyboard=kmx</code> or <code>?keyboard=js</code> to the URL..
The default is to use the .kmx version.
</p>
<h3>Type Khmer in this text area:</h3>
<textarea id='inputarea' class='test' placeholder='Type here'></textarea>
<h3><a href="./">Return to testing home page</a></h3>
</div>

View file

@ -20,6 +20,7 @@ builder_describe "Runs the Keyman Engine for Web unit-testing suites" \
"test+" \
":dom Runs DOM-oriented unit tests (reduced footprint, nothing browser-specific)" \
":integrated Runs KMW's integration test suite" \
":e2e Runs KMW's end-to-end test suite" \
"--inspect Runs browser-based unit tests in an inspectable mode"
builder_parse "$@"
@ -30,6 +31,7 @@ builder_parse "$@"
WTR_CONFIG=
if builder_is_ci_build; then
WTR_CONFIG=.CI
export KEYMAN_IS_CI_BUILD=1
fi
# Prepare the flags for the karma command.
@ -42,6 +44,8 @@ fi
cd "${KEYMAN_ROOT}"
builder_run_action test:dom web-test-runner --config "web/src/test/auto/dom/web-test-runner${WTR_CONFIG}.config.mjs" ${WTR_INSPECT}
builder_run_action test:dom web-test-runner --config "web/src/test/auto/dom/web-test-runner${WTR_CONFIG}.config.mjs" ${WTR_INSPECT}
builder_run_action test:integrated web-test-runner --config "web/src/test/auto/integrated/web-test-runner${WTR_CONFIG}.config.mjs" ${WTR_INSPECT}
builder_run_action test:integrated web-test-runner --config "web/src/test/auto/integrated/web-test-runner${WTR_CONFIG}.config.mjs" ${WTR_INSPECT}
builder_run_action test:e2e npx playwright test --config "web/src/test/auto/e2e/playwright.config.ts"