Skip to content

Commit

Permalink
chore: move smoketest script into the e2e suite (#12414)
Browse files Browse the repository at this point in the history
* chore: moved smoke test to e2e test; added nx task

* fix: promisify npm install

* fix: remove environment variable

* fix: remove yargs

* fix: out of sync yarn lock

* fix: corrected rest api waits

* chore: add rc and latest scripts for smoketest

* fix: add defaults for env vars
  • Loading branch information
sdstolworthy authored Apr 21, 2023
1 parent 2fc630a commit 2044fc0
Show file tree
Hide file tree
Showing 4 changed files with 308 additions and 1 deletion.
4 changes: 4 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
"inputs": ["default", "^default"],
"dependsOn": ["^package"]
},
"smoketest": {
"inputs": ["default", "^production"],
"dependsOn": ["build"]
},
"test": {
"inputs": ["default", "^production"],
"dependsOn": ["build"]
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
"rm-dev-link": "rimraf -f \"$(yarn global bin)/amplify-dev\"",
"setup-dev-win": "yarn dev-build && yarn link-win && yarn link-aa-win",
"setup-dev": "yarn dev-build && yarn rm-dev-link && yarn link-dev && yarn rm-aa-dev-link && yarn link-aa-dev",
"smoketest": "nx run-many --parallel=1 --target=smoketest --all --output-style=stream-without-prefixes",
"smoketest@latest": "CLI_VERSION=latest nx run-many --parallel=1 --target=smoketest --all --output-style=stream-without-prefixes",
"smoketest@rc": "CLI_VERSION=rc nx run-many --parallel=1 --target=smoketest --all --output-style=stream-without-prefixes",
"split-e2e-tests": "yarn ts-node ./scripts/split-e2e-tests.ts && git add .circleci/config.yml",
"test-changed": "nx affected --target=test",
"test-ci": "nx run-many --parallel=4 --target=test --all -- --ci --runInBand",
Expand Down
3 changes: 2 additions & 1 deletion packages/amplify-e2e-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"general-config-e2e": "jest src/__tests__/general-config/general-config-headless-init.test.ts --verbose",
"build-tests": "tsc --build tsconfig.tests.json",
"setup-profile": "ts-node ./src/configure_tests.ts",
"clean-e2e-resources": "ts-node ./src/cleanup-e2e-resources.ts"
"clean-e2e-resources": "ts-node ./src/cleanup-e2e-resources.ts",
"smoketest": "jest --runInBand --testPathPattern='smoketest'"
},
"dependencies": {
"@aws-amplify/amplify-category-auth": "3.0.3",
Expand Down
299 changes: 299 additions & 0 deletions packages/amplify-e2e-tests/src/__tests__/smoketest.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,299 @@
import execa from 'execa';
import * as path from 'path';
import * as fs from 'fs-extra';
import * as os from 'os';
import * as pty from 'node-pty';
import { nspawn as spawn } from '@aws-amplify/amplify-e2e-core';
jest.retryTimes(0);

export const NPM = {
install(pkgName: string, isGlobal: boolean = false): Promise<void> {
return new Promise((resolve) => {
const args = ['install'];
if (isGlobal) {
args.push('-g');
}
args.push(pkgName);
execa.sync('npm', args, { stdio: 'inherit' });
resolve();
});
},
uninstall(pkgName: string, isGlobal: boolean = false): Promise<void> {
return new Promise((resolve) => {
const args = ['uninstall', pkgName];
if (isGlobal) {
args.push('-g');
}
execa.sync('npm', args, { stdio: 'inherit' });
resolve();
});
},
};

export type SmoketestArgs = {
projectDirectory: string;
cliVersion: string;
destructive: boolean;
};

function getArgs(): SmoketestArgs {
const { DESTRUCTIVE = 'false', CLI_VERSION = 'latest', PROJECT_DIRECTORY = path.join(os.tmpdir(), 'smoketest') } = process.env;
return {
projectDirectory: PROJECT_DIRECTORY,
cliVersion: CLI_VERSION,
destructive: DESTRUCTIVE.toLowerCase() === 'true',
};
}

export function assertEmpty(directory: string) {
const contents = fs.readdirSync(directory);
if (contents.length > 0) {
throw new Error(`Project directory ${directory} is not empty`);
}
}

export function createProjectDirectory(projectDirectory: string) {
fs.ensureDirSync(projectDirectory);
}

export function removeProjectDirectory(projectDirectory: string) {
fs.removeSync(projectDirectory);
}

export type Evaluatable = {
evaluate(data: string, ptyProcess: pty.IPty): Promise<boolean>;
};
export class Amplify {
private executionArgs: { cwd: string; encoding: 'utf8' };
constructor(projectDirectory: string) {
this.executionArgs = { cwd: projectDirectory, encoding: 'utf8' };
}
init = async () => {
const args = ['init', '-y'];
return spawn('amplify', args, this.executionArgs).runAsync();
};
delete = async () => {
const args = ['delete', '--force'];
return spawn('amplify', args, this.executionArgs).runAsync();
};
addApi = async () => {
return spawn('amplify', ['add', 'api'], this.executionArgs)
.wait('Select from one of the below mentioned services:')
.sendCarriageReturn()
.wait('Here is the GraphQL API that we will create.')
.sendCarriageReturn()
.wait('Choose a schema template')
.sendCarriageReturn()
.wait('Do you want to edit the schema now')
.sendNo()
.runAsync();
};
push = async () => {
return spawn('amplify', ['push', '-y'], this.executionArgs).runAsync();
};
pull = async (appId?: string, envName?: string) => {
const args = ['pull', '-y'];
if (appId) {
args.push('--appId', appId);
}
if (envName) {
args.push('--envName', envName);
}
return spawn('amplify', args, this.executionArgs).runAsync();
};
addAuth = () => {
return spawn('amplify', ['add', 'auth'], this.executionArgs)
.wait('Do you want to use the default authentication and security configuration?')
.sendCarriageReturn()
.wait('How do you want users to be able to sign in?')
.sendCarriageReturn()
.wait('Do you want to configure advanced settings?')
.sendCarriageReturn()
.runAsync();
};
updateAuth = () => {
return spawn('amplify', ['update', 'auth'], this.executionArgs)
.wait('What do you want to do?')
.sendCarriageReturn()
.wait('What domain name prefix do you want to use?')
.sendCarriageReturn()
.wait('Enter your redirect signin URI:')
.send('http://localhost:3000/')
.sendCarriageReturn()
.wait('Do you want to add another redirect signin URI')
.sendCarriageReturn()
.wait('Enter your redirect signout URI:')
.send('http://localhost:3000/')
.sendCarriageReturn()
.wait('Do you want to add another redirect signout URI')
.sendCarriageReturn()
.wait('Select the identity providers you want to configure for your user pool:')
.send(' ')
.sendCarriageReturn()
.wait('Enter your Facebook App ID for your OAuth flow:')
.send('1234567890')
.sendCarriageReturn()
.wait('Enter your Facebook App Secret for your OAuth flow:')
.send('1234567890')
.sendCarriageReturn()
.runAsync();
};
addRestApi = () => {
return spawn('amplify', ['add', 'api'], { ...this.executionArgs, noOutputTimeout: 3000 })
.wait('Select from one of the below mentioned services')
.sendKeyDown()
.sendCarriageReturn()
.wait('Provide a friendly name')
.delay(500)
.sendCarriageReturn()
.wait('Provide a path')
.sendCarriageReturn()
.wait('Choose a Lambda source')
.sendCarriageReturn()
.wait('Provide an AWS Lambda function name:')
.sendCarriageReturn()
.wait('Choose the runtime that you want to use:')
.sendCarriageReturn()
.wait('Choose the function template that you want to use:')
.sendCarriageReturn()
.wait('Do you want to configure advanced settings?')
.sendLine('n')
.wait('Do you want to edit the local lambda function now?')
.sendLine('n')
.wait('Restrict API access?')
.sendLine('n')
.wait('Do you want to add another path?')
.sendNo()

.runAsync();
};
status = () => {
return spawn('amplify', ['status'], this.executionArgs).runAsync();
};
modifyGraphQlSchema = (schema: string) => {
try {
const [apiName] = fs.readdirSync(path.join(this.executionArgs.cwd, 'amplify', 'backend', 'api'));
const targetPath = path.join(this.executionArgs.cwd, 'amplify', 'backend', 'api', apiName, 'schema.graphql');
fs.writeFileSync(targetPath, schema);
return Promise.resolve(0);
} catch (e) {
return Promise.reject(1);
}
};
}

type Command = {
run: () => Promise<any>;
description: string;
};

const newGraphqlSchema = `
input AMPLIFY { globalAuthRule: AuthRule = { allow: public } } # FOR TESTING ONLY!
type Todo @model {
id: ID!
name: String!
description: String
helloWorld: String
}
`;

function writeBanner(text: string) {
const count = text.length;
const textPadding = 3;
process.stdout.write('\n');
process.stdout.write('#'.repeat(count + 2 + textPadding * 2));
process.stdout.write('\n');
process.stdout.write(`#${' '.repeat(count + textPadding * 2)}#`);
process.stdout.write('\n');
process.stdout.write(`#${' '.repeat(textPadding)}${text}${' '.repeat(textPadding)}#`);
process.stdout.write('\n');
process.stdout.write(`#${' '.repeat(count + textPadding * 2)}#`);
process.stdout.write('\n');
process.stdout.write('#'.repeat(count + 2 + textPadding * 2));
process.stdout.write('\n');
process.stdout.write('\n');
}

describe('Release Smoke Tests', () => {
const createCommands = (amplify: Amplify, cliVersion: string): Command[] => [
{
description: `Install @aws-amplify/cli@${cliVersion}`,
run: () => NPM.install(`@aws-amplify/cli@${cliVersion}`, true),
},
{
description: 'Create an Amplify project',
run: () => amplify.init(),
},
{
description: 'Add an API to the Amplify project',
run: () => amplify.addApi(),
},
{
description: 'Get project status',
run: () => amplify.status(),
},
{
description: 'Add Auth to the Amplify project',
run: () => amplify.addAuth(),
},
{
description: 'Get project status',
run: () => amplify.status(),
},
{
description: 'Push the Amplify project',
run: () => amplify.push(),
},
{
description: 'Get project status',
run: () => amplify.status(),
},
{
description: 'Modify the GraphQL schema',
run: () => amplify.modifyGraphQlSchema(newGraphqlSchema),
},
{
description: 'Update Auth',
run: () => amplify.updateAuth(),
},
{
description: 'Push the Amplify project',
run: () => amplify.push(),
},
{
description: 'Add REST API',
run: () => amplify.addRestApi(),
},
{
description: 'Get project status',
run: () => amplify.status(),
},
{
description: 'Push the Amplify project',
run: () => amplify.push(),
},
{
description: 'Delete the Amplify project',
run: () => amplify.delete(),
},
];
test('An amplify project can be created without error', async () => {
const args = getArgs();

const amplify = new Amplify(args.projectDirectory);
createProjectDirectory(args.projectDirectory);
if (args.destructive) {
writeBanner('Deleting contents of ' + args.projectDirectory);
fs.emptyDirSync(args.projectDirectory);
}
assertEmpty(args.projectDirectory);

const commands = createCommands(amplify, args.cliVersion);
for (const command of commands) {
writeBanner(command.description);
await command.run();
}
});
});

0 comments on commit 2044fc0

Please sign in to comment.