소스 검색

Add E2E browser test (#1492)

Lutz Roeder 7 달 전
부모
커밋
95c6a9ae9d
5개의 변경된 파일189개의 추가작업 그리고 10개의 파일을 삭제
  1. 30 9
      package.js
  2. 65 0
      test/browser.spec.js
  3. 0 0
      test/desktop.spec.js
  4. 75 0
      test/electron.spec.js
  5. 19 1
      test/playwright.config.js

+ 30 - 9
package.js

@@ -513,15 +513,33 @@ const lint = async () => {
 };
 };
 
 
 const test = async (target) => {
 const test = async (target) => {
-    if (target === 'playwright' || read('playwright')) {
-        await exec('npx playwright install --with-deps');
-        const command = "npx playwright test --config=test/playwright.config.js";
-        if (process.platform === 'linux' && (process.env.GITHUB_ACTIONS || process.env.CI)) {
-            await exec(`xvfb-run -a ${command}`);
-        } else {
-            await exec(command);
+    let models = true;
+    for (const name of ['desktop', 'browser']) {
+        /* eslint-disable no-await-in-loop */
+        if (target === name || read(name)) {
+            models = false;
+            switch (name) {
+                case 'desktop': {
+                    await exec('npx playwright install --with-deps');
+                    const host = process.platform === 'linux' && (process.env.GITHUB_ACTIONS || process.env.CI) ? 'xvfb-run -a ' : '';
+                    await exec(`${host}npx playwright test --config=test/playwright.config.js --project=desktop`);
+                    break;
+                }
+                case 'browser': {
+                    if (process.platform !== 'win32') {
+                        await exec('npx playwright install --with-deps');
+                        const headed = process.env.GITHUB_ACTIONS || process.env.CI ? '' :  ' --headed';
+                        await exec(`npx playwright test --config=test/playwright.config.js --project=browser${headed}`);
+                    }
+                    break;
+                }
+                default: {
+                    break;
+                }
+            }
         }
         }
-    } else {
+    }
+    if (models) {
         target = target || args.join(' ');
         target = target || args.join(' ');
         await exec(`node test/models.js ${target}`);
         await exec(`node test/models.js ${target}`);
     }
     }
@@ -532,7 +550,10 @@ const validate = async () => {
     await lint();
     await lint();
     writeLine('test');
     writeLine('test');
     await test('tag:validation');
     await test('tag:validation');
-    await test('playwright');
+    writeLine('test desktop');
+    await test('desktop');
+    writeLine('test browser');
+    await test('browser');
 };
 };
 
 
 const update = async () => {
 const update = async () => {

+ 65 - 0
test/browser.spec.js

@@ -0,0 +1,65 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import * as playwright from '@playwright/test';
+import * as url from 'url';
+
+playwright.test.setTimeout(120_000);
+
+playwright.test('browser', async ({ page }) => {
+
+    const self = url.fileURLToPath(import.meta.url);
+    const dir = path.dirname(self);
+    const file = path.resolve(dir, '../third_party/test/onnx/candy.onnx');
+    playwright.expect(fs.existsSync(file)).toBeTruthy();
+
+    // Navigate to the application
+    await page.goto('/');
+
+    playwright.expect(page).toBeDefined();
+    await page.waitForLoadState('domcontentloaded');
+
+    const consent = await page.locator('#message-button');
+    if (await consent.isVisible({ timeout: 2000 }).catch(() => false)) {
+        await consent.click();
+    }
+
+    // Wait for the welcome screen to be ready
+    await page.waitForSelector('body.welcome', { timeout: 5000 });
+
+    // Set up file chooser promise before clicking
+    const fileChooserPromise = page.waitForEvent('filechooser');
+    const openButton = await page.locator('.open-file-button, button:has-text("Open Model")');
+    await openButton.click();
+    const fileChooser = await fileChooserPromise;
+    await fileChooser.setFiles(file);
+
+    // Wait for the graph to render
+    await page.waitForSelector('#canvas', { state: 'attached', timeout: 10000 });
+    await page.waitForSelector('body.default', { timeout: 10000 });
+
+    // Open find sidebar
+    const isMac = process.platform === 'darwin';
+    await page.keyboard.press(isMac ? 'Meta+F' : 'Control+F');
+    await page.waitForTimeout(500);
+    const search = await page.waitForSelector('#search', { state: 'visible', timeout: 5000 });
+    playwright.expect(search).toBeDefined();
+
+    // Find and activate tensor
+    await search.fill('convolution1_W');
+    await page.waitForSelector('.sidebar-find-content li', { state: 'attached' });
+    const item = await page.waitForSelector('.sidebar-find-content li:has-text("convolution1_W")');
+    await item.dblclick();
+
+    // Expand the 'value' field
+    const valueEntry = await page.waitForSelector('#sidebar-content .sidebar-item:has(.sidebar-item-name input[value="value"])');
+    const valueButton = await valueEntry.waitForSelector('.sidebar-item-value-button');
+    await valueButton.click();
+
+    // Check first number from tensor value
+    const pre = await valueEntry.waitForSelector('pre');
+    const text = (await pre.textContent()) || '';
+    const match = text.match(/-?\d+(?:\.\d+)?(?:e[+-]?\d+)?/i);
+    playwright.expect(match).not.toBeNull();
+    const first = parseFloat(match[0]);
+    playwright.expect(first).toBe(0.1353299617767334);
+});

+ 0 - 0
test/playwright.spec.js → test/desktop.spec.js


+ 75 - 0
test/electron.spec.js

@@ -0,0 +1,75 @@
+
+import * as fs from 'fs';
+import * as path from 'path';
+import * as playwright from '@playwright/test';
+import * as url from 'url';
+
+playwright.test.setTimeout(120_000);
+
+playwright.test('desktop', async () => {
+
+    const self = url.fileURLToPath(import.meta.url);
+    const dir = path.dirname(self);
+    const file = path.resolve(dir, '../third_party/test/onnx/candy.onnx');
+    playwright.expect(fs.existsSync(file)).toBeTruthy();
+
+    // Launch app
+    const electron = await playwright._electron;
+    const args = ['.', '--no-sandbox'];
+    const app = await electron.launch({ args });
+    const window = await app.firstWindow();
+
+    playwright.expect(window).toBeDefined();
+    await window.waitForLoadState('domcontentloaded');
+
+    const consent = await window.locator('#message-button');
+    if (await consent.isVisible({ timeout: 2000 }).catch(() => false)) {
+        await consent.click();
+    }
+
+    // Open the model
+    await app.evaluate(async (electron, location) => {
+        const windows = electron.BrowserWindow.getAllWindows();
+        if (windows.length > 0) {
+            const [window] = windows;
+            window.webContents.send('open', { path: location });
+        }
+    }, file);
+
+    // Wait for the graph to render
+    await window.waitForSelector('#canvas', { state: 'attached', timeout: 10000 });
+    await window.waitForSelector('body.default', { timeout: 10000 });
+
+    // Open find sidebar
+    await app.evaluate(async (electron) => {
+        const windows = electron.BrowserWindow.getAllWindows();
+        if (windows.length > 0) {
+            const [window] = windows;
+            window.webContents.send('find', {});
+        }
+    });
+    await window.waitForTimeout(500);
+    const search = await window.waitForSelector('#search', { state: 'visible', timeout: 5000 });
+    playwright.expect(search).toBeDefined();
+
+    // Find and activate tensor
+    await search.fill('convolution1_W');
+    await window.waitForSelector('.sidebar-find-content li', { state: 'attached' });
+    const item = await window.waitForSelector('.sidebar-find-content li:has-text("convolution1_W")');
+    await item.dblclick();
+
+    // Expand the 'value' field
+    const valueEntry = await window.waitForSelector('#sidebar-content .sidebar-item:has(.sidebar-item-name input[value="value"])');
+    const valueButton = await valueEntry.waitForSelector('.sidebar-item-value-button');
+    await valueButton.click();
+
+    // Check first number from tensor value
+    const pre = await valueEntry.waitForSelector('pre');
+    const text = (await pre.textContent()) || '';
+    const match = text.match(/-?\d+(?:\.\d+)?(?:e[+-]?\d+)?/i);
+    playwright.expect(match).not.toBeNull();
+    const first = parseFloat(match[0]);
+    playwright.expect(first).toBe(0.1353299617767334);
+
+    await app.close();
+});

+ 19 - 1
test/playwright.config.js

@@ -2,5 +2,23 @@
 import playwright from '@playwright/test';
 import playwright from '@playwright/test';
 
 
 export default playwright.defineConfig({
 export default playwright.defineConfig({
-    outputDir: '../dist/test-results'
+    outputDir: '../dist/test-results',
+    webServer: {
+        command: 'npm run server',
+        port: 8080,
+        timeout: 120 * 1000
+    },
+    projects: [
+        {
+            name: 'desktop',
+            testMatch: '**/desktop.spec.js',
+        },
+        {
+            name: 'browser',
+            testMatch: '**/browser.spec.js',
+            use: {
+                baseURL: 'http://localhost:8080'
+            },
+        },
+    ],
 });
 });