test: Address flaky setup e2e (no-changelog) (#6085)

* test: Add /setup intercept for `skipSetup` command (no-changelog)

* Drop all tables for e2e reset, intercept account setup request

* Fix linting issues

* Allow to skip setup account request intercept and linting fixes

* Make sure variables are loaded

* Use PATCH for enabling of e2e features

* Do not exclude migration table from truncation

* Add user sign-up intercept
This commit is contained in:
OlegIvaniv 2023-05-03 14:06:06 +02:00 committed by GitHub
parent 700cc39cbc
commit e88232ede2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 65 additions and 35 deletions

View file

@ -11,7 +11,7 @@ module.exports = defineConfig({
}, },
defaultCommandTimeout: 10000, defaultCommandTimeout: 10000,
requestTimeout: 12000, requestTimeout: 12000,
numTestsKeptInMemory: 0, numTestsKeptInMemory: 2,
experimentalMemoryManagement: true, experimentalMemoryManagement: true,
e2e: { e2e: {
baseUrl: BASE_URL, baseUrl: BASE_URL,
@ -35,8 +35,13 @@ module.exports = defineConfig({
return null return null
} }
}, },
'enable-feature': (feature) => 'set-feature': ({ feature, enabled }) => {
fetch(BASE_URL + `/e2e/enable-feature/${feature}`, { method: 'POST' }), return fetch(BASE_URL + `/e2e/feature/${feature}`, {
method: 'PATCH',
body: JSON.stringify({ enabled }),
headers: { 'Content-Type': 'application/json' }
})
},
}); });
}, },
}, },

View file

@ -16,6 +16,7 @@ describe('Variables', () => {
}); });
it('should show the unlicensed action box when the feature is disabled', () => { it('should show the unlicensed action box when the feature is disabled', () => {
cy.disableFeature('feat:variables');
cy.signin({ email, password }); cy.signin({ email, password });
cy.visit(variablesPage.url); cy.visit(variablesPage.url);
@ -30,7 +31,10 @@ describe('Variables', () => {
beforeEach(() => { beforeEach(() => {
cy.signin({ email, password }); cy.signin({ email, password });
cy.intercept('GET', '/rest/variables').as('loadVariables');
cy.visit(variablesPage.url); cy.visit(variablesPage.url);
cy.wait(['@loadVariables', '@loadSettings']);
}); });
it('should show the licensed action box when the feature is enabled', () => { it('should show the licensed action box when the feature is enabled', () => {

View file

@ -84,11 +84,12 @@ describe('Default owner', () => {
}); });
it('should be able to setup instance and migrate workflows and credentials', () => { it('should be able to setup instance and migrate workflows and credentials', () => {
cy.setup({ email, firstName, lastName, password }); cy.setup({ email, firstName, lastName, password }, true);
messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential'); messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential');
messageBox.actions.confirm(); messageBox.actions.confirm();
cy.wait('@setupRequest');
cy.url().should('include', settingsUsersPage.url); cy.url().should('include', settingsUsersPage.url);
settingsSidebar.actions.back(); settingsSidebar.actions.back();

View file

@ -105,18 +105,22 @@ Cypress.Commands.add('signup', ({ firstName, lastName, password, url }) => {
signupPage.getters.form().within(() => { signupPage.getters.form().within(() => {
cy.url().then((url) => { cy.url().then((url) => {
cy.intercept('/rest/users/*').as('userSignup')
signupPage.getters.firstName().type(firstName); signupPage.getters.firstName().type(firstName);
signupPage.getters.lastName().type(lastName); signupPage.getters.lastName().type(lastName);
signupPage.getters.password().type(password); signupPage.getters.password().type(password);
signupPage.getters.submit().click(); signupPage.getters.submit().click();
cy.wait('@userSignup');
}); });
}); });
}); });
Cypress.Commands.add('setup', ({ email, firstName, lastName, password }) => { Cypress.Commands.add('setup', ({ email, firstName, lastName, password }, skipIntercept = false) => {
const signupPage = new SignupPage(); const signupPage = new SignupPage();
cy.intercept('GET', signupPage.url).as('setupPage');
cy.visit(signupPage.url); cy.visit(signupPage.url);
cy.wait('@setupPage');
signupPage.getters.form().within(() => { signupPage.getters.form().within(() => {
cy.url().then((url) => { cy.url().then((url) => {
@ -125,7 +129,13 @@ Cypress.Commands.add('setup', ({ email, firstName, lastName, password }) => {
signupPage.getters.firstName().type(firstName); signupPage.getters.firstName().type(firstName);
signupPage.getters.lastName().type(lastName); signupPage.getters.lastName().type(lastName);
signupPage.getters.password().type(password); signupPage.getters.password().type(password);
cy.intercept('POST', '/rest/owner/setup').as('setupRequest');
signupPage.getters.submit().click(); signupPage.getters.submit().click();
if(!skipIntercept) {
cy.wait('@setupRequest');
}
} else { } else {
cy.log('User already signed up'); cy.log('User already signed up');
} }
@ -168,7 +178,9 @@ Cypress.Commands.add('skipSetup', () => {
const workflowPage = new WorkflowPage(); const workflowPage = new WorkflowPage();
const Confirmation = new MessageBox(); const Confirmation = new MessageBox();
cy.intercept('GET', signupPage.url).as('setupPage');
cy.visit(signupPage.url); cy.visit(signupPage.url);
cy.wait('@setupPage');
signupPage.getters.form().within(() => { signupPage.getters.form().within(() => {
cy.url().then((url) => { cy.url().then((url) => {
@ -199,7 +211,11 @@ Cypress.Commands.add('setupOwner', (payload) => {
}); });
Cypress.Commands.add('enableFeature', (feature) => { Cypress.Commands.add('enableFeature', (feature) => {
cy.task('enable-feature', feature); cy.task('set-feature', { feature, enabled: true });
});
Cypress.Commands.add('disableFeature', (feature) => {
cy.task('set-feature', { feature, enabled: false });
}); });
Cypress.Commands.add('grantBrowserPermissions', (...permissions: string[]) => { Cypress.Commands.add('grantBrowserPermissions', (...permissions: string[]) => {

View file

@ -36,13 +36,14 @@ declare global {
signin(payload: SigninPayload): void; signin(payload: SigninPayload): void;
signout(): void; signout(): void;
signup(payload: SignupPayload): void; signup(payload: SignupPayload): void;
setup(payload: SetupPayload): void; setup(payload: SetupPayload, skipIntercept?: boolean): void;
setupOwner(payload: SetupPayload): void; setupOwner(payload: SetupPayload): void;
inviteUsers(payload: InviteUsersPayload): void; inviteUsers(payload: InviteUsersPayload): void;
interceptREST(method: string, url: string): Chainable<Interception>; interceptREST(method: string, url: string): Chainable<Interception>;
skipSetup(): void; skipSetup(): void;
resetAll(): void; resetAll(): void;
enableFeature(feature: string): void; enableFeature(feature: string): void;
disableFeature(feature: string): void;
waitForLoad(waitForIntercepts?: boolean): void; waitForLoad(waitForIntercepts?: boolean): void;
grantBrowserPermissions(...permissions: string[]): void; grantBrowserPermissions(...permissions: string[]): void;
readClipboard(): Chainable<string>; readClipboard(): Chainable<string>;

View file

@ -36,33 +36,30 @@ type Feature = keyof typeof enabledFeatures;
Container.get(License).isFeatureEnabled = (feature: Feature) => enabledFeatures[feature] ?? false; Container.get(License).isFeatureEnabled = (feature: Feature) => enabledFeatures[feature] ?? false;
const tablesToTruncate = [ const tablesNotToTruncate = ['sqlite_sequence'];
'auth_identity',
'auth_provider_sync_history',
'event_destinations',
'shared_workflow',
'shared_credentials',
'webhook_entity',
'workflows_tags',
'credentials_entity',
'tag_entity',
'workflow_statistics',
'workflow_entity',
'execution_entity',
'settings',
'installed_packages',
'installed_nodes',
'user',
'role',
];
const truncateAll = async () => { const truncateAll = async () => {
const connection = Db.getConnection(); const connection = Db.getConnection();
for (const table of tablesToTruncate) { const allTables: Array<{ name: string }> = await connection.query(
"SELECT name FROM sqlite_master WHERE type='table';",
);
// Disable foreign key constraint checks
await connection.query('PRAGMA foreign_keys = OFF;');
for (const { name: table } of allTables) {
try {
if (tablesNotToTruncate.includes(table)) continue;
await connection.query( await connection.query(
`DELETE FROM ${table}; DELETE FROM sqlite_sequence WHERE name=${table};`, `DELETE FROM ${table}; DELETE FROM sqlite_sequence WHERE name=${table};`,
); );
} catch (error) {
console.warn('Dropping Table for E2E Reset error: ', error);
} }
}
// Re-enable foreign key constraint checks
await connection.query('PRAGMA foreign_keys = ON;');
}; };
const setupUserManagement = async () => { const setupUserManagement = async () => {
@ -139,8 +136,14 @@ e2eController.post('/db/setup-owner', bodyParser.json(), async (req, res) => {
res.writeHead(204).end(); res.writeHead(204).end();
}); });
e2eController.post('/enable-feature/:feature', async (req: Request<{ feature: Feature }>, res) => { e2eController.patch(
'/feature/:feature',
bodyParser.json(),
async (req: Request<{ feature: Feature }>, res) => {
const { feature } = req.params; const { feature } = req.params;
enabledFeatures[feature] = true; const { enabled } = req.body;
enabledFeatures[feature] = enabled === undefined || enabled === true;
res.writeHead(204).end(); res.writeHead(204).end();
}); },
);