mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2025-04-08 02:31:36 +02:00
extend test cases for enhanced safety around event handlers
This commit is contained in:
parent
84b2c48d0d
commit
7546fb083f
3 changed files with 100 additions and 6 deletions
|
@ -5,11 +5,12 @@
|
|||
{{template "base/alert" .}}
|
||||
|
||||
{{/* Refresh the list every interval (30s) unless the document isn't visible or a dropdown is open; refresh
|
||||
if visibility changes as well. */}}
|
||||
if visibility changes as well. simulate-polling-interval is a custom event used for e2e tests to mimic
|
||||
the polling interval and should be defined identically to the `every` clause for accurate testing. */}}
|
||||
<div
|
||||
hx-get="?workflow={{$.CurWorkflow}}&actor={{$.CurActor}}&status={{$.CurStatus}}&page={{$.Page.Paginater.Current}}&list_inner=true"
|
||||
hx-swap="morph:innerHTML"
|
||||
hx-trigger="every 30s [document.visibilityState === 'visible' && noActiveDropdowns()], visibilitychange[document.visibilityState === 'visible'] from:document"
|
||||
hx-trigger="every 30s [pollingOk()], visibilitychange[document.visibilityState === 'visible'] from:document, simulate-polling-interval[pollingOk()] from:document"
|
||||
hx-indicator="#reloading-indicator">
|
||||
{{template "repo/actions/list_inner" .}}
|
||||
</div>
|
||||
|
@ -18,6 +19,10 @@
|
|||
|
||||
<script type="text/javascript">
|
||||
|
||||
function pollingOk() {
|
||||
return document.visibilityState === 'visible' && noActiveDropdowns();
|
||||
}
|
||||
|
||||
// Intent: If the "Actor" or "Status" dropdowns are currently open and being navigated, or the workflow dispatch
|
||||
// dropdown form is open, the htmx refresh would replace them with closed dropdowns. Instead this prevents the list
|
||||
// refresh from occurring while those dropdowns are open.
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<div id="reloading-indicator" class="htmx-indicator"></div>
|
||||
|
||||
<!-- Actor -->
|
||||
<div class="ui{{if not .Actors}} disabled{{end}} dropdown jump item">
|
||||
<div id="actor_dropdown" class="ui{{if not .Actors}} disabled{{end}} dropdown jump item">
|
||||
<span class="text">{{ctx.Locale.Tr "actions.runs.actor"}}</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
|
@ -42,7 +42,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- Status -->
|
||||
<div class="ui dropdown jump item">
|
||||
<div id="status_dropdown" class="ui dropdown jump item">
|
||||
<span class="text">{{ctx.Locale.Tr "actions.runs.status"}}</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
|
|
|
@ -81,6 +81,18 @@ test('workflow dispatch box not available for unauthenticated users', async ({pa
|
|||
await save_visual(page);
|
||||
});
|
||||
|
||||
async function simulatePollingInterval(page) {
|
||||
// In order to simulate the background page sitting around for > 30s, a custom event `simulate-polling-interval` is
|
||||
// fired into the document to mimic the polling interval expiring -- although this isn't a perfectly great E2E test
|
||||
// with this kind of mimicry, it's better than having multiple >30s execution-time tests.
|
||||
|
||||
// No other way to trigger a client-side event other than using browser-side eval:
|
||||
// eslint-disable-next-line playwright/no-eval
|
||||
await page.$eval('html', () => {
|
||||
document.dispatchEvent(new Event('simulate-polling-interval'));
|
||||
});
|
||||
}
|
||||
|
||||
test.describe('workflow list dynamic refresh', () => {
|
||||
test.use({user: 'user2'});
|
||||
|
||||
|
@ -99,10 +111,87 @@ test.describe('workflow list dynamic refresh', () => {
|
|||
// No other way to trigger a visibilitychange event other than using browser-side eval:
|
||||
// eslint-disable-next-line playwright/no-eval
|
||||
await backgroundPage.$eval('html', () => {
|
||||
const ev = new Event('visibilitychange');
|
||||
document.dispatchEvent(ev);
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
});
|
||||
await expect(backgroundPage.locator('.run-list>:first-child .run-list-meta', {hasText: 'now'})).toBeVisible();
|
||||
await save_visual(backgroundPage);
|
||||
});
|
||||
|
||||
test('refreshes on interval', async ({page}, testInfo) => {
|
||||
// Test operates by creating two pages; one which is sitting idle on the workflows list (backgroundPage), and one
|
||||
// which triggers a workflow dispatch. After the polling, the page should refresh and show the newly dispatched
|
||||
// workflow from the other page.
|
||||
|
||||
const backgroundPage = await (await page.context()).newPage();
|
||||
await backgroundPage.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
||||
|
||||
// Mirror the `Workflow Authenticated user2 > dispatch success` test:
|
||||
await dispatchSuccess(page, testInfo);
|
||||
|
||||
await simulatePollingInterval(page);
|
||||
await expect(backgroundPage.locator('.run-list>:first-child .run-list-meta', {hasText: 'now'})).toBeVisible();
|
||||
await save_visual(backgroundPage);
|
||||
});
|
||||
|
||||
test('post-refresh the dropdowns continue to operate', async ({page}, testInfo) => {
|
||||
// Verify that after the page is dynamically refreshed, the 'Actor', 'Status', and 'Run workflow' dropdowns work
|
||||
// correctly -- that the htmx morph hasn't messed up any JS event handlers.
|
||||
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
||||
await simulatePollingInterval(page);
|
||||
|
||||
// Mirror the `Workflow Authenticated user2 > dispatch success` test -- this creates data for the 'Actor' dropdown
|
||||
await dispatchSuccess(page, testInfo);
|
||||
|
||||
// Workflow run dialog
|
||||
await expect(page.locator('input[name="inputs[string2]"]')).toBeHidden();
|
||||
await page.locator('#workflow_dispatch_dropdown>button').click();
|
||||
await expect(page.locator('input[name="inputs[string2]"]')).toBeVisible();
|
||||
await page.locator('#workflow_dispatch_dropdown>button').click();
|
||||
|
||||
// Status dropdown
|
||||
await expect(page.getByText('Waiting')).toBeHidden();
|
||||
await expect(page.getByText('Failure')).toBeHidden();
|
||||
await page.locator('#status_dropdown').click();
|
||||
await expect(page.getByText('Waiting')).toBeVisible();
|
||||
await expect(page.getByText('Failure')).toBeVisible();
|
||||
|
||||
// Actor dropdown
|
||||
await expect(page.getByText('All actors')).toBeHidden();
|
||||
await page.locator('#actor_dropdown').click();
|
||||
await expect(page.getByText('All Actors')).toBeVisible();
|
||||
});
|
||||
|
||||
test('refresh does not break interacting with open drop-downs', async ({page}) => {
|
||||
// Verify that if the polling refresh occurs while interacting with any multi-step dropdown on the page, the
|
||||
// multi-step interaction continues to be visible and functional. This is implemented by preventing the refresh,
|
||||
// but that isn't the subject of the test here -- as long as the dropdown isn't broken by the refresh, that's fine.
|
||||
await page.goto('/user2/test_workflows/actions?workflow=test-dispatch.yml&actor=0&status=0');
|
||||
|
||||
// Mirror the `Workflow Authenticated user2 > dispatch success` test -- this creates data for the 'Actor' dropdown
|
||||
// await dispatchSuccess(page, testInfo);
|
||||
|
||||
// Workflow run dialog
|
||||
await expect(page.locator('input[name="inputs[string2]"]')).toBeHidden();
|
||||
await page.locator('#workflow_dispatch_dropdown>button').click();
|
||||
await expect(page.locator('input[name="inputs[string2]"]')).toBeVisible();
|
||||
await simulatePollingInterval(page);
|
||||
await expect(page.locator('input[name="inputs[string2]"]')).toBeVisible();
|
||||
|
||||
// Status dropdown
|
||||
await expect(page.getByText('Waiting')).toBeHidden();
|
||||
await expect(page.getByText('Failure')).toBeHidden();
|
||||
await page.locator('#status_dropdown').click();
|
||||
await expect(page.getByText('Waiting')).toBeVisible();
|
||||
await expect(page.getByText('Failure')).toBeVisible();
|
||||
await simulatePollingInterval(page);
|
||||
await expect(page.getByText('Waiting')).toBeVisible();
|
||||
await expect(page.getByText('Failure')).toBeVisible();
|
||||
|
||||
// Actor dropdown
|
||||
await expect(page.getByText('All actors')).toBeHidden();
|
||||
await page.locator('#actor_dropdown').click();
|
||||
await expect(page.getByText('All Actors')).toBeVisible();
|
||||
await simulatePollingInterval(page);
|
||||
await expect(page.getByText('All Actors')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue