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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); ci(e2e): harden staging e2e against deterministic and flaky reds by jacekradko · Pull Request #8756 · clerk/javascript · GitHub
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
2 changes: 2 additions & 0 deletions .changeset/staging-e2e-resilience-p0.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
---
---
21 changes: 19 additions & 2 deletions .github/workflows/e2e-staging.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -33,7 +33,11 @@ permissions:
actions: write

concurrency:
group: ${{ github.workflow }}-${{ github.event.inputs.ref || github.event.client_payload.ref || 'main' }}
# Key on the clerk_go commit being validated rather than the (effectively always "main")
# ref, so distinct staging deploys no longer cancel each other and each commit can report
# its own result. Duplicate dispatches for the SAME commit still de-dupe; manual dispatches
# without a commit SHA fall back to the unique run_id and are never cancelled.
group: ${{ github.workflow }}-${{ github.event.inputs.clerk-go-commit-sha || github.event.client_payload.clerk-go-commit-sha || github.run_id }}
cancel-in-progress: true

jobs:
Expand DownExpand Up@@ -118,7 +122,9 @@ jobs:
defaults:
run:
shell: bash
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 15 }}
# Must stay above the 25-minute "Run Integration Tests" step budget below, otherwise the
# job-level cap kills the run mid-suite and reports a misleading timeout/cancellation.
timeout-minutes: ${{ vars.TIMEOUT_MINUTES_LONG && fromJSON(vars.TIMEOUT_MINUTES_LONG) || 30 }}

strategy:
fail-fast: false
Expand DownExpand Up@@ -293,6 +299,17 @@ jobs:
path: test-results
retention-days: 1

# Always upload the machine-readable report (even on success) so downstream
# reporting/classification can run regardless of the leg's pass/fail outcome.
- name: Upload Playwright JSON report
if: ${{ !cancelled() }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: playwright-report-${{ github.run_id }}-${{ github.run_attempt }}-${{ steps.inputs.outputs.artifact-suffix }}
path: playwright-report/results.json
if-no-files-found: ignore
retention-days: 3

report:
name: Report Results
needs: [integration-tests]
Expand Down
5 changes: 5 additions & 0 deletions integration/playwright.config.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -27,6 +27,11 @@ export const common: PlaywrightTestConfig = {
export default defineConfig({
...common,

// Emit a machine-readable report in CI so the staging workflow's report job can
// classify failures (flaky vs failed, infra vs regression) instead of reading a
// single pass/fail boolean. Local runs keep the default human-readable list output.
reporter: process.env.CI ? [['list'], ['json', { outputFile: 'playwright-report/results.json' }]] : 'list',

projects: [
{
name: 'setup',
Expand Down
12 changes: 12 additions & 0 deletions integration/tests/custom-pages.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand DownExpand Up@@ -172,6 +173,17 @@ testAgainstRunningApps({ withPattern: ['react.vite.withEmailCodes'] })(
});

test('custom profile page survives a parent rerender without remounting', async ({ page, context }) => {
// Validates the @clerk/react fix from #8604 (custom pages must not remount on a
// parent rerender). The staging leg installs published @latest packages
// (E2E_SDK_SOURCE=latest), which do not yet contain the fix, so this is
// deterministically red there until the next @clerk/react release. PR CI builds
// the SDK from the branch and still exercises this test.
// TODO: remove this skip once @clerk/react including #8604 is published to npm.
test.skip(
process.env.E2E_SDK_SOURCE === 'latest',
'validates the unreleased @clerk/react fix (#8604); covered by ref-built CI',
);

const u = createTestUtils({ app, page, context });
await u.po.signIn.goTo();
await u.po.signIn.waitForMounted();
Expand Down
9 changes: 9 additions & 0 deletions integration/tests/whatsapp-phone-code.test.ts
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
/* eslint-disable turbo/no-undeclared-env-vars */
import { expect, test } from '@playwright/test';

import type { Application } from '../models/application';
Expand All@@ -6,6 +7,14 @@ import type { FakeUser } from '../testUtils';
import { createTestUtils } from '../testUtils';

test.describe('sign up and sign in with WhatsApp phone code @generic', () => {
// The WhatsApp alternate phone-code channel is not provisioned on the staging
// instance, so the WhatsApp sign-up button never renders there and every test in
// this suite times out deterministically (no amount of retrying helps). Unlike the
// long-running-app suites, this test builds its own app via `app.withEnv(...)` and
// therefore bypasses the `isStagingReady` graceful-skip. Skip it explicitly on
// staging until the channel is enabled on the staging mirror.
test.skip(process.env.E2E_STAGING === '1', 'WhatsApp channel is not enabled on the staging instance');

const configs = [appConfigs.next.appRouter];

configs.forEach(config => {
Expand Down
Loading