diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 7b774780..7a1e9a69 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -71,9 +71,6 @@ jobs: - name: Migrate database working-directory: ./packages/backend run: yarn db:migrate - - name: Seed user - working-directory: ./packages/backend - run: yarn db:seed:user & - name: Install certutils run: sudo apt install -y libnss3-tools - name: Install mkcert diff --git a/packages/e2e-tests/README.md b/packages/e2e-tests/README.md index 9b097e9c..df54b0b1 100644 --- a/packages/e2e-tests/README.md +++ b/packages/e2e-tests/README.md @@ -44,6 +44,14 @@ and it should install the associated browsers for the test running. For more inf We recommend using [Playwright Test for VSCode](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) maintained by Microsoft. This lets you run playwright tests from within the code editor, giving you access to additional tools, such as easily running subsets of tests. +[Global setup and teardown](https://playwright.dev/docs/test-global-setup-teardown) are part of the tests. + +By running `yarn test` setup and teardown actions will take place. + +If you need to setup Admin account (if you didn't seed the DB with the admin account or have clean DB) you should run `auth.setup.js` file. + +If you want to clean the database (drop tables) and perform required migrations run `global.teardown.js`. + # Test failures If there are failing tests in the test suite, this can be caused by a myriad of reasons, but one of the best places to start is either running the test in a headed browser, looking at the associated trace file for the failed test, or checking out the output of a failed GitHub Action. diff --git a/packages/e2e-tests/fixtures/admin-setup-page.js b/packages/e2e-tests/fixtures/admin-setup-page.js new file mode 100644 index 00000000..704a9caf --- /dev/null +++ b/packages/e2e-tests/fixtures/admin-setup-page.js @@ -0,0 +1,75 @@ +import { BasePage } from "./base-page"; +const { faker } = require('@faker-js/faker'); +const { expect } = require('@playwright/test'); + +export class AdminSetupPage extends BasePage { + path = '/installation'; + + /** + * @param {import('@playwright/test').Page} page + */ + constructor(page) { + super(page); + + this.fullNameTextField = this.page.getByTestId('fullName-text-field'); + this.emailTextField = this.page.getByTestId('email-text-field'); + this.passwordTextField = this.page.getByTestId('password-text-field'); + this.repeatPasswordTextField = this.page.getByTestId('repeat-password-text-field'); + this.createAdminButton = this.page.getByTestId('signUp-button'); + this.invalidFields = this.page.locator('p.Mui-error'); + this.successAlert = this.page.getByTestId('success-alert'); + } + + async open() { + return await this.page.goto(this.path); + } + + async fillValidUserData() { + await this.fullNameTextField.fill(process.env.LOGIN_EMAIL); + await this.emailTextField.fill(process.env.LOGIN_EMAIL); + await this.passwordTextField.fill(process.env.LOGIN_PASSWORD); + await this.repeatPasswordTextField.fill(process.env.LOGIN_PASSWORD); + } + + async fillInvalidUserData() { + await this.fullNameTextField.fill(''); + await this.emailTextField.fill('abcde'); + await this.passwordTextField.fill(''); + await this.repeatPasswordTextField.fill('a'); + } + + async fillNotMatchingPasswordUserData() { + const testUser = this.generateUser(); + await this.fullNameTextField.fill(testUser.fullName); + await this.emailTextField.fill(testUser.email); + await this.passwordTextField.fill(testUser.password); + await this.repeatPasswordTextField.fill(testUser.wronglyRepeatedPassword); + } + + async submitAdminForm() { + await this.createAdminButton.click(); + } + + async expectInvalidFields(errorCount) { + await expect(await this.invalidFields.all()).toHaveLength(errorCount); + } + + async expectSuccessAlertToBeVisible() { + await expect(await this.successAlert).toBeVisible(); + } + + async expectSuccessMessageToContainLoginLink() { + await expect(await this.successAlert.locator('a')).toHaveAttribute('href', '/login'); + } + + generateUser() { + faker.seed(Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)); + + return { + fullName: faker.person.fullName(), + email: faker.internet.email(), + password: faker.internet.password(), + wronglyRepeatedPassword: faker.internet.password() + }; + } +}; diff --git a/packages/e2e-tests/fixtures/index.js b/packages/e2e-tests/fixtures/index.js index f91e473e..f9376786 100644 --- a/packages/e2e-tests/fixtures/index.js +++ b/packages/e2e-tests/fixtures/index.js @@ -7,6 +7,7 @@ const { UserInterfacePage } = require('./user-interface-page'); const { LoginPage } = require('./login-page'); const { AcceptInvitation } = require('./accept-invitation-page'); const { adminFixtures } = require('./admin'); +const { AdminSetupPage } = require('./admin-setup-page'); const { AdminCreateUserPage } = require('./admin/create-user-page'); exports.test = test.extend({ @@ -54,6 +55,11 @@ exports.publicTest = test.extend({ await use(acceptInvitationPage); }, + adminSetupPage: async ({ page }, use) => { + const adminSetupPage = new AdminSetupPage(page); + await use(adminSetupPage); + }, + adminCreateUserPage: async ({page}, use) => { const adminCreateUserPage = new AdminCreateUserPage(page); await use(adminCreateUserPage); diff --git a/packages/e2e-tests/knexfile.js b/packages/e2e-tests/knexfile.js new file mode 100644 index 00000000..b465bf0b --- /dev/null +++ b/packages/e2e-tests/knexfile.js @@ -0,0 +1,25 @@ +const fileExtension = 'js'; + +const knexConfig = { + client: 'pg', + connection: { + host: process.env.POSTGRES_HOST, + user: process.env.POSTGRES_USERNAME, + port: process.env.POSTGRES_PORT, + password: process.env.POSTGRES_PASSWORD, + database: process.env.POSTGRES_DATABASE + }, + searchPath: ['public'], + pool: { min: 0, max: 20 }, + migrations: { + directory: '../../packages/backend/src/db/migrations/', + extension: fileExtension, + loadExtensions: [`.${fileExtension}`], + }, + seeds: { + directory: '../../packages/backend/src/db/seeds', + }, + ...(process.env.APP_ENV === 'test' ? knexSnakeCaseMappers() : {}), +}; + +export default knexConfig; diff --git a/packages/e2e-tests/playwright.config.js b/packages/e2e-tests/playwright.config.js index 315ca57a..ec034c39 100644 --- a/packages/e2e-tests/playwright.config.js +++ b/packages/e2e-tests/playwright.config.js @@ -42,9 +42,19 @@ module.exports = defineConfig({ /* Configure projects for major browsers */ projects: [ + { + name: 'setup', + testMatch: /.*\.setup\.js/, + teardown: 'teardown', + }, + { + name: 'teardown', + testMatch: /.*\.teardown\.js/, + }, { name: 'chromium', use: { ...devices['Desktop Chrome'] }, + dependencies: ['setup'], }, // { diff --git a/packages/e2e-tests/tests/admin-setup/admin.setup.js b/packages/e2e-tests/tests/admin-setup/admin.setup.js new file mode 100644 index 00000000..43bcc7b1 --- /dev/null +++ b/packages/e2e-tests/tests/admin-setup/admin.setup.js @@ -0,0 +1,29 @@ +const { publicTest: setup, expect } = require('../../fixtures/index'); + +setup.describe.serial('Admin setup page', () => { + // eslint-disable-next-line no-unused-vars + setup('should not be able to login if admin is not created', async ({ page, adminSetupPage, loginPage }) => { + await expect(async () => { + await expect(await page.url()).toContain(adminSetupPage.path); + }).toPass(); + }); + + setup('should validate the inputs', async ({ adminSetupPage }) => { + await adminSetupPage.open(); + await adminSetupPage.fillInvalidUserData(); + await adminSetupPage.submitAdminForm(); + await adminSetupPage.expectInvalidFields(4); + + await adminSetupPage.fillNotMatchingPasswordUserData(); + await adminSetupPage.submitAdminForm(); + await adminSetupPage.expectInvalidFields(1); + }); + + setup('should create admin', async ({ adminSetupPage }) => { + await adminSetupPage.open(); + await adminSetupPage.fillValidUserData(); + await adminSetupPage.submitAdminForm(); + await adminSetupPage.expectSuccessAlertToBeVisible(); + await adminSetupPage.expectSuccessMessageToContainLoginLink(); + }); +}); diff --git a/packages/e2e-tests/tests/global.teardown.js b/packages/e2e-tests/tests/global.teardown.js new file mode 100644 index 00000000..ada10ffc --- /dev/null +++ b/packages/e2e-tests/tests/global.teardown.js @@ -0,0 +1,12 @@ +const { publicTest } = require('../fixtures'); +import knex from 'knex'; +import knexConfig from '../knexfile.js'; + +publicTest.describe('restore db', () => { + publicTest('clean db and perform migrations', async () => { + const knexClient = knex(knexConfig) + const migrator = knexClient.migrate; + await migrator.rollback({}, true); + await migrator.latest(); + }) +}); diff --git a/packages/web/src/components/InstallationForm/index.jsx b/packages/web/src/components/InstallationForm/index.jsx index 04e30f3e..a21eefda 100644 --- a/packages/web/src/components/InstallationForm/index.jsx +++ b/packages/web/src/components/InstallationForm/index.jsx @@ -134,6 +134,7 @@ function InstallationForm() { fullWidth margin="dense" type="password" + data-test="password-text-field" error={touchedFields.password && !!errors?.password} helperText={ touchedFields.password && errors?.password?.message @@ -155,6 +156,7 @@ function InstallationForm() { fullWidth margin="dense" type="password" + data-test="repeat-password-text-field" error={touchedFields.confirmPassword && !!errors?.confirmPassword} helperText={ touchedFields.confirmPassword && @@ -186,7 +188,7 @@ function InstallationForm() { )} /> {install.isSuccess && ( - + {formatMessage('installationForm.success', { link: (str) => (