diff --git a/.github/workflows/js.yml b/.github/workflows/js.yml index 180f7c2..107ab7b 100644 --- a/.github/workflows/js.yml +++ b/.github/workflows/js.yml @@ -86,8 +86,50 @@ jobs: with: platform: js - release: + test: needs: build + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - { os: ubuntu-24.04 } + - { os: ubuntu-24.04-arm } + - { os: macos-26 } + - { os: windows-2025 } + steps: + - uses: actions/checkout@v5 + + - uses: actions/setup-node@v6 + with: + node-version: 24.x + + - name: Download artifact + uses: actions/download-artifact@v5 + with: + path: build/js + + - name: Install dependencies + run: | + curl -LO --output-dir cache https://github.com/fcitx-contrib/fcitx5-online/releases/download/latest/fcitx5-online.zip + unzip cache/fcitx5-online.zip + npm i -g pnpm + pnpm i + npx playwright install + npx playwright install-deps + + - name: Test + if: ${{ startsWith(matrix.os, 'macos') || startsWith(matrix.os, 'windows') }} + run: pnpm run test + + - name: Test + if: ${{ startsWith(matrix.os, 'ubuntu') }} + run: | + pnpm run test:chromium + pnpm run test:firefox + + release: + needs: test if: ${{ github.ref == 'refs/heads/master' }} runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index dd5b3dc..64b6931 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ cache/js meta-*.json *.gz *.bz2 +node_modules +pnpm-lock.yaml +*.tsbuildinfo +fcitx5-online* +test-results diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..0aa96e7 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,3 @@ +import antfu from '@antfu/eslint-config' + +export default antfu({}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..035e8c2 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "fcitx5-plugins", + "type": "module", + "scripts": { + "lint": "eslint tests", + "lint:fix": "eslint --fix tests", + "check": "tsc --noEmit", + "test": "playwright test --browser all", + "test:chromium": "playwright test --browser chromium", + "test:firefox": "playwright test --browser firefox", + "test:webkit": "playwright test --browser webkit" + }, + "devDependencies": { + "@antfu/eslint-config": "^6.0.0", + "@playwright/test": "^1.56.1", + "eslint": "^9.38.0", + "serve": "^14.2.5", + "typescript": "^5.9.3" + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..1d3a18c --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from '@playwright/test' + +export default defineConfig({ + testDir: 'tests', + fullyParallel: true, + webServer: { + command: 'npx serve -l 9000 -S fcitx5-online', + port: 9000, + reuseExistingServer: true, + }, +}) diff --git a/tests/test-mozc.spec.ts b/tests/test-mozc.spec.ts new file mode 100644 index 0000000..a7bd3fd --- /dev/null +++ b/tests/test-mozc.spec.ts @@ -0,0 +1,18 @@ +import { test } from '@playwright/test' +import { expectCandidate, expectText, init } from './util' + +test('Mizu', async ({ page }) => { + test.skip(page.context().browser()!.browserType().name() === 'chromium', 'Chromium rejects big wasm executable.') + await init(page, 'mozc', 'Mozc') + + await page.keyboard.type('m') + await page.keyboard.type('i') + await page.keyboard.type('z') + await page.keyboard.type('u') + await expectCandidate(page, '水') + await expectText(page, 'みず') + + await page.keyboard.press(' ') + await page.keyboard.press('Enter') + await expectText(page, '水') +}) diff --git a/tests/util.ts b/tests/util.ts new file mode 100644 index 0000000..48dbac6 --- /dev/null +++ b/tests/util.ts @@ -0,0 +1,27 @@ +import type { Page } from '@playwright/test' +import { expect } from '@playwright/test' + +export async function init(page: Page, plugin: string, im: string) { + await page.goto('http://localhost:9000') + + // Install plugin. + await page.locator('.my-column > :first-child div:nth-child(6)').click() + const [fileChooser] = await Promise.all([ + page.waitForEvent('filechooser'), + page.locator('.n-upload-trigger').click(), + ]) + await fileChooser.setFiles([`build/js/${plugin}.zip`]) + await page.keyboard.press('Escape') + + // Select IM. + await page.locator('.my-column > :first-child .n-select').click() + await page.locator('.n-base-select-option').getByText(im).click() +} + +export function expectCandidate(page: Page, text: string) { + return expect(page.locator('.fcitx-candidate').first().locator('.fcitx-text')).toHaveText(new RegExp(`^${text}$`)) +} + +export function expectText(page: Page, text: string) { + return expect(page.locator('.my-column > :nth-child(2) textarea')).toHaveValue(text) +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..626af97 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "skipLibCheck": true + }, + "include": ["tests/**/*.ts"] +}