Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
8 changes: 3 additions & 5 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ on:
# Nightly workflow - Run at 2 AM UTC daily
schedule:
- cron: '0 2 * * *'
# Post-merge - Run after changes are merged to main
push:
branches: [main]
# Manual trigger - Support workflow_dispatch for on-demand runs
workflow_dispatch:
inputs:
Expand All @@ -32,9 +29,10 @@ on:
type: string
jobs:
e2e-tests:
# E2E tests run only on the current LTS Node version for stability and speed.
strategy:
matrix:
node-version: [22.x, 24.x]
node-version: [22.x]
runs-on: ubuntu-latest
environment: e2e-dev
timeout-minutes: 25
Expand Down Expand Up @@ -129,7 +127,7 @@ jobs:
retention-days: 30

- name: Notify on Failure
if: failure() && (github.event_name == 'schedule' || github.event_name == 'push') && steps.check-secrets.outputs.has-secrets == 'true'
if: failure() && github.event_name == 'schedule' && steps.check-secrets.outputs.has-secrets == 'true'
uses: actions/github-script@v7
with:
script: |
Expand Down
31 changes: 28 additions & 3 deletions packages/b2c-cli/test/functional/e2e/sites-operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ describe('Sites Operations E2E Tests', function () {
let serverHostname: string;
let ownSandboxId: null | string = null;

async function sleep(ms: number): Promise<void> {
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
}

before(async function () {
if (!process.env.SFCC_CLIENT_ID || !process.env.SFCC_CLIENT_SECRET) {
this.skip();
Expand Down Expand Up @@ -66,13 +72,32 @@ describe('Sites Operations E2E Tests', function () {
console.log(`Created dedicated sandbox ${ownSandboxId} at ${serverHostname}`);
}

const importResult = await runCLI(['job', 'import', SITE_ARCHIVE_PATH, '--server', serverHostname]);
async function runImportWithRetry(remainingRetries: number) {
const importResult = await runCLI(['job', 'import', SITE_ARCHIVE_PATH, '--server', serverHostname]);
if (importResult.exitCode === 0) return importResult;

const msg = importResult.stderr || importResult.stdout;
const isTransient = /fetch failed|ECONNRESET|ETIMEDOUT/i.test(msg);

if (!isTransient || remainingRetries <= 0) return importResult;

console.warn(
`Sites E2E: transient import error, retrying after delay (remaining retries: ${remainingRetries}):`,
msg,
);
await sleep(2000);
return runImportWithRetry(remainingRetries - 1);
}

const importResult = await runImportWithRetry(2);

if (importResult.exitCode !== 0) {
const msg = importResult.stderr || importResult.stdout;
// If the sandbox/client lacks permissions, treat this as a valid customer scenario
// and skip the suite rather than failing in before().
if (/not\s+allowed|unauthorized|forbidden|401|403/i.test(msg)) {
// and skip the suite rather than failing in before(). Also skip on transient
// network issues where the underlying HTTP fetch fails.
if (/not\s+allowed|unauthorized|forbidden|401|403|fetch failed/i.test(msg)) {
console.warn('Sites E2E: skipping suite due to import error:', msg);
this.skip();
}
expect(importResult.exitCode).to.equal(0, msg);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@ describe('WebDAV Operations E2E Tests', function () {
if (result.exitCode !== 0) return false;
const response = JSON.parse(result.stdout);
return response.entries?.some((e: any) => entryName(e) === testFileName);
});
}, 300_000);
});
});
});
Loading