From 6d46c291ef13ef7f851f257a1101040bc9c602e7 Mon Sep 17 00:00:00 2001 From: wojteknowacki Date: Thu, 16 Nov 2023 20:57:36 +0100 Subject: [PATCH] reset password test --- .github/workflows/e2e-pool.yml | 92 +++++++++++++++++++ cypress/e2e/staffMembers.js | 2 +- playwright/api/mailpit.ts | 12 +++ .../data/{testData.ts => e2eTestData.ts} | 7 ++ playwright/data/url.ts | 1 + playwright/pages/loginPage.ts | 16 ++++ playwright/pages/setUpNewPasswordPage.ts | 34 +++++++ playwright/tests/auth.setup.ts | 25 +++-- playwright/tests/product.spec.ts | 2 +- playwright/tests/shippingMethods.spec.ts | 2 +- playwright/tests/staffMembers.spec.ts | 52 ++++++++++- .../ResetPasswordSuccessPage.tsx | 1 + 12 files changed, 235 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/e2e-pool.yml rename playwright/data/{testData.ts => e2eTestData.ts} (81%) create mode 100644 playwright/pages/setUpNewPasswordPage.ts diff --git a/.github/workflows/e2e-pool.yml b/.github/workflows/e2e-pool.yml new file mode 100644 index 00000000000..7abce341fea --- /dev/null +++ b/.github/workflows/e2e-pool.yml @@ -0,0 +1,92 @@ +name: E2E pool + +on: [pull_request] + +jobs: + instances: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Retrieve instance + id: retrieve_instance + env: + SALEOR_CLI_ENV: staging + run: | + echo "Getting instance"... + npx saleor login --headless --token=${{ secrets.STAGING_API_TOKEN }} + NAME=$(node scripts/pick-e2e-instance.js | jq -r .name) + echo "POOL_NAME=$NAME" >> $GITHUB_OUTPUT + echo "POOL_INSTANCE=https://$NAME.staging.saleor.cloud" >> $GITHUB_OUTPUT + outputs: + pollIntance: + run-tests: + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + shard: [1/2, 2/2] + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run tests + env: + API_URI: "${{ needs.instances.outputs.POOL_INSTANCE }}/graphql/" + API_URI: "${{ steps.retrieve_instance.outputs.POOL_INSTANCE }}/graphql/" + BASE_URL: "${{ steps.retrieve_instance.outputs.POOL_INSTANCE }}/dashboard/" + E2E_USER_NAME: ${{ secrets.CYPRESS_USER_NAME }} + E2E_USER_PASSWORD: ${{ secrets.CYPRESS_USER_PASSWORD }} + E2E_PERMISSIONS_USERS_PASSWORD: ${{ secrets.CYPRESS_PERMISSIONS_USERS_PASSWORD }} + run: | + echo "Running tests on: $API_URI" + echo "Base url $BASE_URL" + npx playwright test --shard ${{ matrix.shard }} + - name: Release instance + if: always() + run: node scripts/release-e2e-instance.js ${{ steps.retrieve_instance.outputs.POOL_NAME }} + - name: Upload blob report to GitHub Actions Artifacts + if: always() + uses: actions/upload-artifact@v3 + with: + name: all-blob-reports + path: blob-report + retention-days: 1 + + merge-reports: + if: always() + needs: [run-tests] + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@v3 + with: + name: all-blob-reports + path: all-blob-reports + + - name: Merge into HTML Report + run: npx playwright merge-reports --reporter html ./all-blob-reports + + - name: Upload HTML report + uses: actions/upload-artifact@v3 + with: + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 14 diff --git a/cypress/e2e/staffMembers.js b/cypress/e2e/staffMembers.js index 51707d01731..497a458a626 100644 --- a/cypress/e2e/staffMembers.js +++ b/cypress/e2e/staffMembers.js @@ -178,7 +178,7 @@ describe("Staff members", () => { ); it( - "should not be able to create staff member with not unique email. TC: SALEOR_3508", + "should not be able to create staff member with not unique email. TC: SALEOR_3508 - should not be migrated to playwright as critical", { tags: ["@staffMembers", "@allEnv", "@critical"] }, () => { const firstName = faker.name.firstName(); diff --git a/playwright/api/mailpit.ts b/playwright/api/mailpit.ts index 3cf688a698d..31f4f80bbfa 100644 --- a/playwright/api/mailpit.ts +++ b/playwright/api/mailpit.ts @@ -1,3 +1,4 @@ +import { URL_LIST } from "@data/url"; import { APIRequestContext, expect } from "@playwright/test"; const MAILPIT_URI = process.env.CYPRESS_MAILPITURL || "no mailpit url provided"; @@ -51,4 +52,15 @@ export class MailpitService { return userEmails; } + + async generateResetPasswordUrl(userEmail: string) { + const tokenRegex = /token=([A-Za-z0-9]+(-[A-Za-z0-9]+)+)/; + + const userEmails = await this.getEmailsForUser(userEmail); + const emailDetails = await this.getEmailDetails(userEmails[0].ID); + const emailHtmlFormat = tokenRegex.exec(emailDetails.HTML.toString()); + const token = "&" + emailHtmlFormat![0]; + const resetPasswordUrl = URL_LIST.resetPassword + userEmail + token; + return resetPasswordUrl; + } } diff --git a/playwright/data/testData.ts b/playwright/data/e2eTestData.ts similarity index 81% rename from playwright/data/testData.ts rename to playwright/data/e2eTestData.ts index fe31e220ed5..1c8600cfcde 100644 --- a/playwright/data/testData.ts +++ b/playwright/data/e2eTestData.ts @@ -30,4 +30,11 @@ export const USERS = { email: "user-to-be-activated@gmai.com", info: "Inactive user used in activation user test", }, + userForPasswordReset: { + email: "user-for-password-reset@gmail.com", + newPassword: "4321test", + info: "User used in reset password test", + name: "e2e", + lastName: "user", + }, }; diff --git a/playwright/data/url.ts b/playwright/data/url.ts index f290b28f41c..8eeeff7f776 100644 --- a/playwright/data/url.ts +++ b/playwright/data/url.ts @@ -35,4 +35,5 @@ export const URL_LIST = { variant: "variant/", warehouses: "warehouses/", webhooksAndEvents: "custom-apps/", + resetPassword: "/new-password/?email=", }; diff --git a/playwright/pages/loginPage.ts b/playwright/pages/loginPage.ts index 8d0391c4e55..449a213b033 100644 --- a/playwright/pages/loginPage.ts +++ b/playwright/pages/loginPage.ts @@ -6,6 +6,9 @@ export class LoginPage { readonly emailInput: Locator; readonly passwordInput: Locator; readonly signInButton: Locator; + readonly resetPasswordLink: Locator; + readonly sendEmailWithResetLinkButton: Locator; + readonly backToLoginPageButton: Locator; homePage: HomePage; constructor(page: Page) { this.page = page; @@ -13,6 +16,19 @@ export class LoginPage { this.emailInput = page.getByTestId("email"); this.passwordInput = page.getByTestId("password"); this.signInButton = page.getByTestId("submit"); + this.resetPasswordLink = page.getByTestId("reset-password-link"); + this.sendEmailWithResetLinkButton = page.getByTestId("submit"); + this.backToLoginPageButton = page.getByTestId("back-to-login-button"); + } + + async clickBackToLoginPageButton() { + await this.backToLoginPageButton.click(); + } + async clickResetPasswordLink() { + await this.resetPasswordLink.click(); + } + async clickSendEmailWithResetLinkButton() { + await this.sendEmailWithResetLinkButton.click(); } async loginAndSetStorageState( diff --git a/playwright/pages/setUpNewPasswordPage.ts b/playwright/pages/setUpNewPasswordPage.ts new file mode 100644 index 00000000000..e6921179380 --- /dev/null +++ b/playwright/pages/setUpNewPasswordPage.ts @@ -0,0 +1,34 @@ +import { MailpitService } from "@api/mailpit"; +import type { APIRequestContext, Locator, Page } from "@playwright/test"; + +export class SetUpNewPasswordPage { + readonly page: Page; + readonly passwordInput: Locator; + readonly confirmPasswordInput: Locator; + readonly setNewPasswordButton: Locator; + readonly mailpitService: MailpitService; + + constructor(page: Page, request: APIRequestContext) { + this.page = page; + this.mailpitService = new MailpitService(request); + this.passwordInput = page.getByTestId("password"); + this.confirmPasswordInput = page.getByTestId("confirm-password"); + this.setNewPasswordButton = page.getByTestId("button-bar-confirm"); + } + + async typePassword(password: string) { + await this.passwordInput.fill(password); + } + async typeConfirmedPassword(password: string) { + await this.confirmPasswordInput.fill(password); + } + async clickSetNewPasswordButton() { + await this.setNewPasswordButton.click(); + } + + async gotoUserResetPasswordPage(userEmail: string) { + const resetPasswordPageUrl = + await this.mailpitService.generateResetPasswordUrl(userEmail); + await this.page.goto(resetPasswordPageUrl); + } +} diff --git a/playwright/tests/auth.setup.ts b/playwright/tests/auth.setup.ts index cb41cd7da2d..ab629708210 100644 --- a/playwright/tests/auth.setup.ts +++ b/playwright/tests/auth.setup.ts @@ -17,15 +17,26 @@ const contentPermissionsFile = "playwright/.auth/content.json"; const channelsWebhooksPermissionsFile = "playwright/.auth/channels-webhooks.json"; const customerWebhooksPermissionsFile = "playwright/.auth/customer.json"; +const unauthenticatedUserPermissionsFile = + "playwright/.auth/unauthenticated-user.json"; -setup("authenticate as admin", async ({ page }) => { +// setup("authenticate as admin", async ({ page }) => { +// const loginPage = await new LoginPage(page); +// await loginPage.loginAndSetStorageState( +// process.env.CYPRESS_USER_NAME!, +// process.env.CYPRESS_USER_PASSWORD!, +// page, +// adminFile, +// ); +// }); +setup("unauthenticated user ", async ({ page }) => { const loginPage = await new LoginPage(page); - await loginPage.loginAndSetStorageState( - process.env.CYPRESS_USER_NAME!, - process.env.CYPRESS_USER_PASSWORD!, - page, - adminFile, - ); + await loginPage.goto(); + await loginPage.resetPasswordLink.waitFor({ state: "visible" }); + // End of authentication steps. + await page + .context() + .storageState({ path: unauthenticatedUserPermissionsFile }); }); // setup("authenticate as user with discount permissions", async ({ page }) => { // const loginPage = new LoginPage(page); diff --git a/playwright/tests/product.spec.ts b/playwright/tests/product.spec.ts index f50a70269ec..aa809a29824 100644 --- a/playwright/tests/product.spec.ts +++ b/playwright/tests/product.spec.ts @@ -1,4 +1,4 @@ -import { PRODUCTS } from "@data/testData"; +import { PRODUCTS } from "@data/e2eTestData"; import { BasePage } from "@pages/basePage"; import { ProductCreateDialog } from "@pages/dialogs/productCreateDialog"; import { ProductPage } from "@pages/productPage"; diff --git a/playwright/tests/shippingMethods.spec.ts b/playwright/tests/shippingMethods.spec.ts index 948e4a85da3..6a021b5e72a 100644 --- a/playwright/tests/shippingMethods.spec.ts +++ b/playwright/tests/shippingMethods.spec.ts @@ -1,4 +1,4 @@ -import { SHIPPING_METHODS } from "@data/testData"; +import { SHIPPING_METHODS } from "@data/e2eTestData"; import { ShippingMethodsPage } from "@pages/shippingMethodsPage"; import { ShippingRatesPage } from "@pages/shippingRatesPage"; import { expect, test } from "@playwright/test"; diff --git a/playwright/tests/staffMembers.spec.ts b/playwright/tests/staffMembers.spec.ts index 409756acc4c..c94c62f75e4 100644 --- a/playwright/tests/staffMembers.spec.ts +++ b/playwright/tests/staffMembers.spec.ts @@ -1,7 +1,10 @@ import { BasicApiService } from "@api/basics"; -import { USERS } from "@data/testData"; +import { USERS } from "@data/e2eTestData"; import { URL_LIST } from "@data/url"; import { ConfigurationPage } from "@pages/configurationPage"; +import { HomePage } from "@pages/homePage"; +import { LoginPage } from "@pages/loginPage"; +import { SetUpNewPasswordPage } from "@pages/setUpNewPasswordPage"; import { StaffMembersPage } from "@pages/staffMembersPage"; import { expect, test } from "@playwright/test"; @@ -97,3 +100,50 @@ test("TC: SALEOR_38 Admin User should be able to activate other user @basic-regr loginViaApiDeactivatedUserResponse.data.tokenCreate.token, ).not.toEqual(null); }); + +test.use({ storageState: "playwright/.auth/unauthenticated-user.json" }); + +test("TC: SALEOR_39 User should be able to reset password @basic-regression @staff-members", async ({ + page, + request, +}) => { + const loginPage = new LoginPage(page); + const homePage = new HomePage(page); + const setUpNewPasswordPage = new SetUpNewPasswordPage(page, request); + const basicApiService = new BasicApiService(request); + + await page.goto("/"); + await loginPage.clickResetPasswordLink(); + await loginPage.typeEmail(USERS.userForPasswordReset.email); + await loginPage.clickSendEmailWithResetLinkButton(); + await loginPage.clickBackToLoginPageButton(); + await loginPage.emailInput.waitFor({ state: "visible" }); + + await setUpNewPasswordPage.gotoUserResetPasswordPage( + USERS.userForPasswordReset.email, + ); + + await setUpNewPasswordPage.typePassword( + USERS.userForPasswordReset.newPassword, + ); + await setUpNewPasswordPage.typeConfirmedPassword( + USERS.userForPasswordReset.newPassword, + ); + await setUpNewPasswordPage.clickSetNewPasswordButton(); + await expect(homePage.welcomeMessage).toBeVisible({ timeout: 10000 }); + await expect(homePage.welcomeMessage).toContainText( + `${USERS.userForPasswordReset.name} ${USERS.userForPasswordReset.lastName}`, + ); + + const userWithNewPasswordLoginResponse = + await basicApiService.logInUserViaApi({ + email: USERS.userForPasswordReset.email, + password: USERS.userForPasswordReset.newPassword, + }); + await expect( + userWithNewPasswordLoginResponse.data.tokenCreate.errors, + ).toEqual([]); + await expect( + userWithNewPasswordLoginResponse.data.tokenCreate.token, + ).not.toEqual(null); +}); diff --git a/src/auth/components/ResetPasswordSuccessPage/ResetPasswordSuccessPage.tsx b/src/auth/components/ResetPasswordSuccessPage/ResetPasswordSuccessPage.tsx index 96901f89320..903f23bb053 100644 --- a/src/auth/components/ResetPasswordSuccessPage/ResetPasswordSuccessPage.tsx +++ b/src/auth/components/ResetPasswordSuccessPage/ResetPasswordSuccessPage.tsx @@ -37,6 +37,7 @@ const ResetPasswordSuccessPage: React.FC< variant="primary" onClick={onBack} type="submit" + data-test-id="back-to-login-button" >