Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/workflows/process-changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -296,3 +296,25 @@ jobs:
with:
name: playwright-report
path: playwright/playwright-report/

playwright-test-polling:
name: 'Playwright Functional Test (Polling)'
runs-on: ubuntu-latest
needs: install-and-build
steps:
- name: 'Checkout Repository'
uses: actions/checkout@v7.0.0
- uses: ./.github/actions/prepare

- name: Install Playwright Chromium
run: npx playwright install --with-deps chromium

- name: Run Playwright tests with polling
run: npm run test:pw:polling

- name: Upload Playwright Polling Report
uses: actions/upload-artifact@v7
if: failure()
with:
name: playwright-report-polling
path: playwright/playwright-report-polling/
13 changes: 9 additions & 4 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,15 @@ To run the predefined tests
5. run `npm run test`

**using Playwright (E2E tests)**:
1. start a fresh visdom server instance on port `8098` using your environment's Python, i.e. `python -m visdom.server -port 8098 -env_path playwright/tmp` (make sure no other instances interfere).
2. run `npx playwright install chromium` on first setup to install the browser.
3. run `npm run build` *or* `npm run dev` to compile the frontend code.
4. run the Playwright test suite: `npm run test:pw`
1. run `npx playwright install chromium` on first setup to install the browser.
2. run `npm run build` *or* `npm run dev` to compile the frontend code.
3. make sure port `8098` is available; Playwright starts an isolated Visdom server automatically.
4. run the WebSocket test suite: `npm run test:pw`.
5. run the same functional tests with frontend polling: `npm run test:pw:polling`.

The polling suite starts Visdom with `-use_frontend_client_polling` and does not
reuse an existing server. Playwright visual regression specs are excluded from
the polling run.

## Issues
We use GitHub issues to track public bugs. Please ensure your description is
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"test:visual": "cypress run --spec './cypress/integration/screenshots.js'",
"test": "cypress run --config excludeSpecPattern=**/*.init.js",
"test:pw": "playwright test --config=playwright.config.js",
"test:pw:polling": "playwright test --config=playwright.polling.config.js",
"test:pw:init": "playwright test --config=playwright.init.config.js",
"test:pw:visual": "playwright test --config=playwright.visual.config.js",
"lint": "eslint js/.",
Expand Down
3 changes: 3 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const { defineConfig, devices } = require('@playwright/test');
*/
module.exports = defineConfig({
testDir: './playwright/tests',
metadata: {
transport: 'websocket',
},
timeout: 30 * 1000,
expect: {
timeout: 5000,
Expand Down
35 changes: 35 additions & 0 deletions playwright.polling.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2017-present, The Visdom Authors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
*/

const { defineConfig } = require('@playwright/test');
const baseConfig = require('./playwright.config');

module.exports = defineConfig({
...baseConfig,
metadata: {
...baseConfig.metadata,
transport: 'polling',
},
outputDir: './playwright/test-results-polling',
reporter: [
['html', { outputFolder: 'playwright/playwright-report-polling' }],
],
testIgnore: ['**/*.init.spec.js', '**/screenshots.spec.js'],
webServer: {
...baseConfig.webServer,
command:
'visdom -port 8098 -env_path playwright/tmp/polling ' +
'-use_frontend_client_polling',
reuseExistingServer: false,
},
projects: baseConfig.projects.map((project) => ({
...project,
name: `${project.name}-polling`,
})),
});
10 changes: 10 additions & 0 deletions playwright/support/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ async function runDemo(page, name, opts = {}) {

async function closeEnvs(page) {
const clearButton = page.locator('.navbar-form .rc-tree-select-clear');

try {
await clearButton.first().waitFor({ state: 'attached', timeout: 10000 });
} catch (error) {
if ((await clearButton.count()) === 0) {
return;
}
throw error;
}

const count = await clearButton.count();
for (let i = 0; i < count; i++) {
await clearButton.nth(i).click({ force: true });
Expand Down
67 changes: 67 additions & 0 deletions playwright/tests/transport.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Copyright 2017-present, The Visdom Authors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
*
*/

const { test, expect } = require('@playwright/test');

function isSocketPath(url, suffix) {
return new URL(url).pathname.endsWith(suffix);
}

function isPollingQuery(request) {
if (
request.method() !== 'POST' ||
!isSocketPath(request.url(), '/socket_wrap')
) {
return false;
}

try {
return request.postDataJSON().message_type === 'query';
} catch (_error) {
return false;
}
}

test('uses the configured frontend transport', async ({ page }, testInfo) => {
const expectedTransport = testInfo.config.metadata.transport;
const socketWrapRequests = [];
const webSocketURLs = [];

page.on('request', (request) => {
if (isSocketPath(request.url(), '/socket_wrap')) {
socketWrapRequests.push(request);
}
});
page.on('websocket', (webSocket) => {
webSocketURLs.push(webSocket.url());
});

await page.goto('/');
await expect(page.getByText('online').first()).toBeVisible();

const usePolling = await page.evaluate(() => window.USE_POLLING);
expect(usePolling).toBe(expectedTransport === 'polling');

if (expectedTransport === 'polling') {
await expect
.poll(() =>
socketWrapRequests.some((request) => request.method() === 'GET')
)
.toBe(true);
await expect.poll(() => socketWrapRequests.some(isPollingQuery)).toBe(true);
expect(webSocketURLs.some((url) => isSocketPath(url, '/socket'))).toBe(
false
);
} else {
await expect
.poll(() => webSocketURLs.some((url) => isSocketPath(url, '/socket')))
.toBe(true);
expect(socketWrapRequests).toHaveLength(0);
}
});
Loading