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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 87 additions & 30 deletions packages/playground/website/playwright/e2e/php-code-snippet.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import type { Page } from '@playwright/test';
* E2E tests for the <php-snippet> web component embed.
* Verifies that:
* - the demo page renders three snippets,
* - clicking Run on the first shows a real progress bar with a caption
* and percent that advance toward 100,
* - clicking Run on the first shows real button progress that advances
* toward 100,
* - the first snippet executes and shows PHP output,
* - subsequent snippets reuse the same Playground runtime (much faster
* than the first boot) and produce their own output.
Expand Down Expand Up @@ -52,10 +52,18 @@ test.describe('php-code-snippet embed', () => {
await expect(snippet.locator('.powered-by')).toContainText(
'PHP Code Snippet powered by WordPress Playground'
);
Comment on lines 52 to 54
await expect(snippet.locator('.powered-by a')).toHaveAttribute(
await expect(
snippet.locator('.powered-by a').nth(0)
).toHaveAttribute(
'href',
'https://playground.wordpress.net/php-code-snippet-demo.html'
);
await expect(
snippet.locator('.powered-by a').nth(1)
).toHaveAttribute('href', 'https://wordpress.org/playground/');
await expect(snippet.locator('.run-shortcut')).toContainText(
/Ctrl\+Enter|Cmd\+Enter/
);
}
});

Expand All @@ -76,29 +84,31 @@ test.describe('php-code-snippet embed', () => {
).toHaveCount(0);
});

test('first Run boots the runtime and shows progress + output', async ({
test('first Run boots the runtime and shows button progress + output', async ({
page,
}) => {
await page.goto(DEMO_URL);
const first = page.locator('php-snippet').nth(0);
const runButton = first.locator('.run');
const runSpinner = first.locator('.run-spinner');
const runPercent = first.locator('.run-percent');

await expect(first.locator('.progress')).toBeHidden();
await first.locator('.run').click();
await expect(first.locator('.progress')).toHaveCount(0);
await runButton.click();

// Progress bar appears with caption + percent text.
await expect(first.locator('.progress')).toBeVisible();
await expect(first.locator('.caption')).not.toHaveText('');
await expect(first.locator('.percent')).toContainText(/%$/);
await expect(runButton).toHaveAttribute('aria-busy', /true/);
await expect(runSpinner).toBeVisible();
await expect(runPercent).toContainText(/%$/);

// The percent advances past 0 (real progress, not just "0%" forever).
await expect
.poll(
async () =>
Number(
(
(await first.locator('.percent').textContent()) ||
'0%'
).replace('%', '')
((await runPercent.textContent()) || '0%').replace(
'%',
''
)
),
{ timeout: 120_000, intervals: [500] }
)
Expand All @@ -112,8 +122,7 @@ test.describe('php-code-snippet embed', () => {
'Hello from PHP'
);

// Progress hides once the run finishes.
await expect(first.locator('.progress')).toBeHidden();
await expect(runButton).not.toHaveAttribute('aria-busy', /true/);
});

test('subsequent snippets reuse the shared runtime', async ({ page }) => {
Expand Down Expand Up @@ -222,7 +231,7 @@ test.describe('php-code-snippet embed', () => {
);
});

test('Run button invokes the snippet on the first click', async ({
test('Run button queues clicks while a snippet is running', async ({
page,
}) => {
await page.goto(DEMO_URL);
Expand All @@ -233,6 +242,7 @@ test.describe('php-code-snippet embed', () => {
snippet._testRunCount = 0;
snippet._runOnce = async function () {
this._testRunCount += 1;
await new Promise((resolve) => setTimeout(resolve, 100));
const outputWrap = this.shadowRoot.querySelector('.output');
const outputBody =
this.shadowRoot.querySelector('.output-body');
Expand All @@ -241,12 +251,16 @@ test.describe('php-code-snippet embed', () => {
};
});

await editable.locator('.run').click();
const runButton = editable.locator('.run');
await runButton.click();
await expect(runButton).toHaveAttribute('aria-busy', /true/);
await expect(runButton).toBeEnabled();
await runButton.click();

await expect(editable.locator('.output-body')).toContainText(
'run-count:1'
'run-count:2'
);
await expect(editable.locator('.run')).toBeEnabled();
await expect(runButton).toBeEnabled();
});

test('Ctrl+Enter and Cmd+Enter run the focused snippet', async ({
Expand Down Expand Up @@ -294,6 +308,44 @@ test.describe('php-code-snippet embed', () => {
}
});

test('output refresh keeps the light result styling', async ({ page }) => {
await page.goto(DEMO_URL);
const editable = page.locator('php-snippet[name="scratch.php"]');
await expect(editable).toBeVisible();

await editable.evaluate((snippet: any) => {
snippet._runOnce = async function () {
const outputWrap = this.shadowRoot.querySelector('.output');
const outputBody =
this.shadowRoot.querySelector('.output-body');
outputBody.textContent = 'light-output-marker';
outputWrap.classList.add('visible');
this._flashOutput(outputBody);
};
});

await editable.locator('.run').click();
await expect(editable.locator('.output-body')).toContainText(
'light-output-marker'
);

const colors = await editable.evaluate((snippet: any) => {
const output = snippet.shadowRoot.querySelector('.output');
const outputBody = snippet.shadowRoot.querySelector('.output-body');
const outputStyles = getComputedStyle(output);
const bodyStyles = getComputedStyle(outputBody);
return {
outputBackground: outputStyles.backgroundColor,
bodyBackground: bodyStyles.backgroundColor,
bodyColor: bodyStyles.color,
};
});

expect(colors.outputBackground).toBe('rgb(255, 255, 255)');
expect(colors.bodyBackground).not.toBe('rgb(13, 17, 23)');
expect(colors.bodyColor).toBe('rgb(36, 41, 47)');
});

test('wp="none" + blueprint installs a PHP toolkit usable from the snippet', async ({
page,
}) => {
Expand All @@ -307,13 +359,14 @@ test.describe('php-code-snippet embed', () => {
element.setAttribute('playground-origin', window.location.origin);
});
// The snippet ships with an expected-output script that pre-fills the
// output panel. Wait for the real run to execute by watching the
// progress bar appear and then disappear.
await snippet.locator('.run').click();
await expect(snippet.locator('.progress')).toBeVisible({
// output panel. Wait for the real run to execute by watching the run
// button enter and exit its busy state.
const runButton = snippet.locator('.run');
await runButton.click();
await expect(runButton).toHaveAttribute('aria-busy', /true/, {
timeout: 30_000,
});
await expect(snippet.locator('.progress')).toBeHidden({
await expect(runButton).not.toHaveAttribute('aria-busy', /true/, {
timeout: 240_000,
});

Expand All @@ -340,6 +393,7 @@ test.describe('php-code-snippet embed', () => {
const runSpinner = editable.locator('.run-spinner');
const runLabel = editable.locator('.run-label');
const runPercent = editable.locator('.run-percent');
const runShortcut = editable.locator('.run-shortcut');

await editable.evaluate((snippet: any) => {
snippet._runOnce = async function (code: string) {
Expand All @@ -362,12 +416,13 @@ test.describe('php-code-snippet embed', () => {
});

await runButton.click();
await expect(runButton).toBeDisabled();
await expect(runButton).toBeEnabled();
await expect(runButton).toHaveAttribute('aria-busy', /true/, {
timeout: 30_000,
});
await expect(runSpinner).toBeVisible();
await expect(runPercent).toBeVisible();
await expect(runShortcut).toBeHidden();
await expect(runLabel).toHaveText('Running');
await expect(runPercent).toHaveText('42%');
await expect(outputBody).toContainText('slow-run-marker', {
Expand All @@ -379,6 +434,7 @@ test.describe('php-code-snippet embed', () => {
});
await expect(runSpinner).toBeHidden();
await expect(runLabel).toHaveText('Run');
await expect(runShortcut).toBeVisible();

await textarea.evaluate((el: HTMLTextAreaElement) => {
el.value = '<?php echo "second-run-marker";';
Expand All @@ -397,21 +453,22 @@ test.describe('php-code-snippet embed', () => {
await page.goto(DEMO_URL);
const snippet = page.locator('php-snippet[name="precomputed.php"]');

await expect(snippet.locator('.progress')).toBeHidden();
await expect(snippet.locator('.progress')).toHaveCount(0);
await expect(snippet.locator('.output')).toBeVisible();
await expect(snippet.locator('.output-body')).toContainText(
'2 + 2 = 4'
);

await snippet.locator('.run').click();
await expect(snippet.locator('.progress')).toBeVisible();
const runButton = snippet.locator('.run');
await runButton.click();
await expect(runButton).toHaveAttribute('aria-busy', /true/);
await expect(snippet.locator('.output')).toBeVisible({
timeout: 240_000,
});
await expect(snippet.locator('.output-body')).toContainText(
'WordPress is awesome.'
);
await expect(snippet.locator('.progress')).toBeHidden();
await expect(runButton).not.toHaveAttribute('aria-busy', /true/);
await expect(
page.locator('iframe[title="PHP Snippet runtime"]')
).toHaveCount(1);
Expand Down
Loading
Loading