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
4 changes: 4 additions & 0 deletions packages/amplify-e2e-core/global-setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Allow people to use `amplify-category-api-e2e-core/global-setup` as a jest globalSetup.
const globalSetup = require('./lib/jest-global-setup');

module.exports = globalSetup.default || globalSetup;
1 change: 1 addition & 0 deletions packages/amplify-e2e-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"clean": "rimraf ./lib"
},
"dependencies": {
"@aws-sdk/client-amplify": "~3.600.0",
"@aws-sdk/client-amplifybackend": "~3.600.0",
"@aws-sdk/client-appsync": "~3.600.0",
"@aws-sdk/client-cloudformation": "~3.600.0",
Expand Down
78 changes: 78 additions & 0 deletions packages/amplify-e2e-core/src/init/ensureGen1PlaceholderApp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import {
AmplifyClient,
ListAppsCommand,
CreateAppCommand,
ListBackendEnvironmentsCommand,
CreateBackendEnvironmentCommand,
} from '@aws-sdk/client-amplify';

/**
* Name of the placeholder Amplify Gen1 app that, when present in an account +
* region, bypasses the Gen1 end-of-life gate during `amplify init`.
*/
export const GEN1_PLACEHOLDER_APP_NAME = 'DoNotDeleteAppToBypassGen1Deprecation';

/**
* Backend environment name required on the placeholder app for the bypass to
* take effect.
*/
export const GEN1_PLACEHOLDER_BACKEND_ENV_NAME = 'test';

const LIST_APPS_PAGE_SIZE = 100;

/**
* Ensures the Gen1 deprecation-bypass placeholder app (and its `test` backend
* environment) exists in the given region so the Gen1 EOL gate never blocks
* `amplify init` during e2e runs, self-healing any prior cleanup deletion.
*
* Idempotent: the app and backend environment are created only when missing.
* Never throws — all failures are logged and swallowed so an already-healthy
* run is never broken by this best-effort setup step. Relies on the ambient
* e2e credentials picked up by the AWS SDK default provider chain.
*
* @param region target AWS region; defaults to `process.env.CLI_REGION`.
*/
export async function ensureGen1PlaceholderApp(region: string = process.env.CLI_REGION): Promise<void> {
if (!region) {
console.log('⚠️ ensureGen1PlaceholderApp: no region provided (CLI_REGION unset); skipping');
return;
}

try {
const client = new AmplifyClient({ region });

let appId: string | undefined;
let nextToken: string | undefined;
do {
const { apps, nextToken: token } = await client.send(new ListAppsCommand({ maxResults: LIST_APPS_PAGE_SIZE, nextToken }));
const existing = (apps ?? []).find((app) => app.name === GEN1_PLACEHOLDER_APP_NAME);
if (existing) {
appId = existing.appId;
break;
}
nextToken = token;
} while (nextToken);

if (!appId) {
const { app } = await client.send(new CreateAppCommand({ name: GEN1_PLACEHOLDER_APP_NAME }));
appId = app?.appId;
console.log(`✅ ensureGen1PlaceholderApp: created placeholder app '${GEN1_PLACEHOLDER_APP_NAME}' (${appId}) in ${region}`);
}

if (!appId) {
console.log(`⚠️ ensureGen1PlaceholderApp: could not resolve placeholder appId in ${region}; skipping backend env`);
return;
}

const { backendEnvironments } = await client.send(new ListBackendEnvironmentsCommand({ appId }));
const hasEnv = (backendEnvironments ?? []).some((env) => env.environmentName === GEN1_PLACEHOLDER_BACKEND_ENV_NAME);
if (!hasEnv) {
await client.send(new CreateBackendEnvironmentCommand({ appId, environmentName: GEN1_PLACEHOLDER_BACKEND_ENV_NAME }));
console.log(
`✅ ensureGen1PlaceholderApp: created backend env '${GEN1_PLACEHOLDER_BACKEND_ENV_NAME}' for placeholder app in ${region}`,
);
}
} catch (err) {
console.log(`⚠️ ensureGen1PlaceholderApp: best-effort setup failed in ${region} (continuing): ${err?.message ?? err}`);
}
}
1 change: 1 addition & 0 deletions packages/amplify-e2e-core/src/init/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export * from './amplifyPull';
export * from './amplifyPush';
export * from './deleteProject';
export * from './initProjectHelper';
export * from './ensureGen1PlaceholderApp';
export * from './adminUI';
export * from './overrideStack';
3 changes: 2 additions & 1 deletion packages/amplify-e2e-core/src/init/initProjectHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ export function initJSProjectWithProfile(cwd: string, settings?: Partial<typeof
.sendYes()
.wait(/Try "amplify add api" to create a backend API and then "amplify (push|publish)" to deploy everything/)
.run((err: Error) => {
console.error(`Result of initJSProjectWithProfile: ${err}`);
if (err) {
console.error(`initJSProjectWithProfile FAILED: ${err}`);
reject(err);
} else {
console.log('initJSProjectWithProfile SUCCESS');
resolve();
}
});
Expand Down
11 changes: 11 additions & 0 deletions packages/amplify-e2e-core/src/jest-global-setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ensureGen1PlaceholderApp } from './init/ensureGen1PlaceholderApp';

/**
* Jest `globalSetup` hook. Runs exactly once per jest invocation (i.e. once per
* e2e shard) before any test file executes or any `amplify init` runs, ensuring
* the Gen1 deprecation-bypass placeholder app exists in the shard's region
* (`process.env.CLI_REGION`). Best-effort and idempotent — never throws.
*/
export default async function globalSetup(): Promise<void> {
await ensureGen1PlaceholderApp(process.env.CLI_REGION);
}
12 changes: 11 additions & 1 deletion packages/amplify-e2e-core/src/utils/nexpect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,8 +432,18 @@ function chain(context: Context): ExecutionContext {
//
return onError(new Error('Command not found: ' + context.command), false);
}
const fullOutput = recordings.length
? recordings
.filter((f) => f[1] === 'o')
.map((f) => f[2])
.join('')
: 'No output';
return onError(
new Error(`Process exited with non zero exit code ${code}.\n\nLast 10 lines: 👇🏽👇🏽👇🏽👇🏽\n\n\n\n\n${lastScreen}\n\n\n👆🏼👆🏼👆🏼👆🏼`),
new Error(
`Process exited with non zero exit code ${code}${
signal ? ` (signal ${signal})` : ''
}.\n\nFull output:👇🏽👇🏽👇🏽👇🏽\n\n${fullOutput}\n\n👆🏼👆🏼👆🏼👆🏼`,
),
false,
);
} else {
Expand Down
1 change: 1 addition & 0 deletions packages/amplify-e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"jest": {
"verbose": false,
"preset": "ts-jest",
"globalSetup": "amplify-category-api-e2e-core/global-setup",
"testRunner": "amplify-category-api-e2e-core/runner",
"testEnvironment": "amplify-category-api-e2e-core/environment",
"transform": {
Expand Down
1 change: 1 addition & 0 deletions packages/graphql-transformers-e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@
"^axios$": "<rootDir>/../../node_modules/axios/dist/node/axios.cjs"
},
"testEnvironment": "../../FixJestEnvironment.js",
"globalSetup": "amplify-category-api-e2e-core/global-setup",
"coveragePathIgnorePatterns": [
"/node_modules",
"/__tests__/"
Expand Down
Loading