feat(client): add tabs support (#52)
* feat(client): add `tabs` support * chore: apply fixes * test: fix types * build: pw again * build: more attempts to improve PW * fix: regressions after refactoring to tabs * style: apply TS style fixes * build: more PW * test: run a problematic test in serial mode * test: no parallel pw * test: fix race condition in PW * test: pw one worker no retry
This commit is contained in:
5
tests/E2E/launch.sh
Normal file → Executable file
5
tests/E2E/launch.sh
Normal file → Executable file
@@ -95,12 +95,12 @@ trap cleanup EXIT
|
||||
cd "$TARGET_DIR"
|
||||
|
||||
echo "Starting main PHP server on port $PORT1..."
|
||||
php artisan serve --host=127.0.0.1 --port="$PORT1" > php_server1.log 2>&1 &
|
||||
PHP_CLI_SERVER_WORKERS=8 php artisan serve --host=127.0.0.1 --port="$PORT1" > php_server1.log 2>&1 &
|
||||
PID1=$!
|
||||
echo "Main PHP server PID: $PID1"
|
||||
|
||||
echo "Starting secondary PHP server on port $PORT2..."
|
||||
php artisan serve --host=127.0.0.1 --port="$PORT2" > php_server2.log 2>&1 &
|
||||
PHP_CLI_SERVER_WORKERS=8 php artisan serve --host=127.0.0.1 --port="$PORT2" > php_server2.log 2>&1 &
|
||||
PID2=$!
|
||||
echo "Secondary PHP server PID: $PID2"
|
||||
|
||||
@@ -135,6 +135,7 @@ append_env_var() {
|
||||
|
||||
append_env_var "APP_URL" "$BACKEND_URL"
|
||||
append_env_var "NIMBUS_RELAY_ENDPOINT" "$SECONDARY_URL"
|
||||
append_env_var "CACHE_STORE" "array"
|
||||
|
||||
echo "Servers are running..."
|
||||
|
||||
|
||||
@@ -14,11 +14,11 @@ import { defineConfig, devices } from '@playwright/test';
|
||||
export default defineConfig({
|
||||
testDir: "./tests",
|
||||
timeout: 160_000,
|
||||
fullyParallel: false,
|
||||
fullyParallel: true,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 1,
|
||||
workers: process.env.CI ? 4 : 8,
|
||||
retries: 0,
|
||||
workers: process.env.CI ? 1 : 8,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: "html",
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
@@ -33,15 +33,22 @@ export default defineConfig({
|
||||
},
|
||||
|
||||
/* Configure projects for major browsers */
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
projects: !process.env.CI
|
||||
? [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
|
||||
{
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
],
|
||||
{
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
name: "chromium",
|
||||
use: { ...devices["Desktop Chrome"] },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -114,7 +114,7 @@ test("Dump and Die visualization sanity checklist", async ({ page }) => {
|
||||
.click();
|
||||
|
||||
await expect(
|
||||
page.locator("#reka-collapsible-content-v-130"),
|
||||
page.locator("#reka-collapsible-content-v-146"),
|
||||
).toMatchAriaSnapshot(`- text: "0: \\"/\\" (1) 1: \\"\\\\\\" (1)"`);
|
||||
|
||||
// dump #3 (runtime object)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { test, expect } from '../core/fixtures';
|
||||
|
||||
test.describe.configure({ mode: 'serial' });
|
||||
|
||||
test('Link Sharing complete workflow', async ({ page, basePage }) => {
|
||||
// Arrange
|
||||
|
||||
@@ -71,13 +73,17 @@ test('Link Sharing complete workflow', async ({ page, basePage }) => {
|
||||
await page.getByRole('button', { name: 'GET /show-logged-in-user' }).click();
|
||||
await basePage.executeRequest();
|
||||
|
||||
// Verify that clearing storage resets the state
|
||||
await page.context().clearCookies();
|
||||
await page.evaluate(() => {
|
||||
// Prevent any further writes to localStorage from the running app (race condition fix)
|
||||
Storage.prototype.setItem = () => { };
|
||||
|
||||
localStorage.clear();
|
||||
sessionStorage.clear();
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
await basePage.goto();
|
||||
|
||||
// Assert - Verify state is empty
|
||||
|
||||
|
||||
66
tests/E2E/tests/tabs.spec.ts
Normal file
66
tests/E2E/tests/tabs.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { test, expect } from '../core/fixtures';
|
||||
|
||||
test('Multiple tabs support and isolated responses', async ({ page, basePage }) => {
|
||||
// Arrange
|
||||
|
||||
await basePage.goto();
|
||||
|
||||
// Act - Open first tab (Route 1)
|
||||
|
||||
await page.getByRole('button', { name: 'verbs' }).click();
|
||||
await page.getByRole('button', { name: 'GET /verbs' }).click();
|
||||
|
||||
// Act - Execute request in first tab
|
||||
|
||||
await basePage.executeRequest();
|
||||
await expect(page.getByTestId('response-status-badge')).toContainText('200');
|
||||
|
||||
// Assert - Verify it appears in Sidebar Tabs
|
||||
const tabs = page.getByTestId('sidebar-tab');
|
||||
await expect(tabs.filter({ hasText: /GET\s*verbs\/verbs/ })).toBeVisible();
|
||||
|
||||
// Act - Open second tab (Route 2)
|
||||
|
||||
await page.getByRole('button', { name: 'PATCH /verbs' }).click();
|
||||
|
||||
// Assert - Verify second tab is created and response is empty
|
||||
|
||||
await expect(tabs.filter({ hasText: /PATCH\s*verbs\/verbs/ })).toBeVisible();
|
||||
await expect(page.getByTestId('response-empty')).toBeVisible();
|
||||
|
||||
// Act - Execute request in second tab
|
||||
|
||||
await basePage.executeRequest();
|
||||
await expect(page.getByTestId('response-status-badge')).toContainText('422');
|
||||
|
||||
// Act - Switch back to first tab via Open Tabs menu
|
||||
|
||||
await tabs.filter({ hasText: /GET\s*verbs\/verbs/ }).click();
|
||||
|
||||
// Assert - Verify isolated response (should be 200 from Route 1)
|
||||
|
||||
await expect(page.getByTestId('response-status-badge')).toContainText('200');
|
||||
|
||||
// Act - Switch back to second tab via Open Tabs menu
|
||||
|
||||
await tabs.filter({ hasText: /PATCH\s*verbs\/verbs/ }).click();
|
||||
|
||||
// Assert - Verify isolated response (should be 422 from Route 2)
|
||||
|
||||
await expect(page.getByTestId('response-status-badge')).toContainText('422');
|
||||
|
||||
// Act - Close a tab
|
||||
|
||||
// Verify all tabs exist before closing
|
||||
await expect(tabs).toHaveCount(2);
|
||||
|
||||
// Hover over the tab to show the close button and click it
|
||||
const getTab = tabs.filter({ hasText: /GET\s*verbs\/verbs/ });
|
||||
await getTab.hover();
|
||||
await getTab.getByTestId('sidebar-tab-close').click();
|
||||
|
||||
// Assert - Verify tab is closed and only the other one remains
|
||||
await expect(tabs).toHaveCount(1);
|
||||
await expect(tabs.filter({ hasText: /GET\s*verbs\/verbs/ })).not.toBeVisible();
|
||||
await expect(tabs.filter({ hasText: /PATCH\s*verbs\/verbs/ })).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user