From 64e638fd5863ab927668c008ea35fc09adb6c92a Mon Sep 17 00:00:00 2001 From: Palash Date: Wed, 13 Jul 2022 20:43:36 +0530 Subject: [PATCH] test: signup page and login page test are updated (#1351) * test: sign-up test are updated * test: fail test of version api is added * test: more test case over signup page is added * test: coverage is added * chore: auth json is updated * test: auth token and refresh token test is updated --- frontend/package.json | 3 +- frontend/playwright.config.ts | 2 + frontend/src/api/user/getVersion.ts | 7 +- frontend/src/constants/api.ts | 3 + frontend/src/pages/SignUp/SignUp.tsx | 4 +- frontend/tests/auth.json | 38 +++ frontend/tests/fixtures/api/login/200.json | 7 + .../tests/fixtures/api/organisation/201.json | 3 + frontend/tests/fixtures/api/register/200.json | 1 + frontend/tests/fixtures/api/register/401.json | 5 + frontend/tests/fixtures/api/userId/200.json | 11 + frontend/tests/fixtures/common.ts | 43 ++++ frontend/tests/fixtures/constant.ts | 8 + frontend/tests/login/fail.spec.ts | 28 +++ frontend/tests/login/index.spec.ts | 49 ++++ frontend/tests/service/index.spec.ts | 22 ++ frontend/tests/signup/index.spec.ts | 225 +++++++++++++++++- frontend/tsconfig.json | 3 +- 18 files changed, 447 insertions(+), 15 deletions(-) create mode 100644 frontend/src/constants/api.ts create mode 100644 frontend/tests/auth.json create mode 100644 frontend/tests/fixtures/api/login/200.json create mode 100644 frontend/tests/fixtures/api/organisation/201.json create mode 100644 frontend/tests/fixtures/api/register/200.json create mode 100644 frontend/tests/fixtures/api/register/401.json create mode 100644 frontend/tests/fixtures/api/userId/200.json create mode 100644 frontend/tests/fixtures/common.ts create mode 100644 frontend/tests/fixtures/constant.ts create mode 100644 frontend/tests/login/fail.spec.ts create mode 100644 frontend/tests/login/index.spec.ts create mode 100644 frontend/tests/service/index.spec.ts diff --git a/frontend/package.json b/frontend/package.json index f3ccdcc8c8..f116fbf379 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,8 +13,9 @@ "jest:coverage": "jest --coverage", "jest:watch": "jest --watch", "postinstall": "is-ci || yarn husky:configure", - "playwright": "playwright test --config=./playwright.config.ts", + "playwright": "NODE_ENV=testing playwright test --config=./playwright.config.ts", "playwright:local:debug": "PWDEBUG=console yarn playwright --headed --browser=chromium", + "playwright:codegen:local":"playwright codegen http://localhost:3301", "husky:configure": "cd .. && husky install frontend/.husky && cd frontend && chmod ug+x .husky/*", "commitlint": "commitlint --edit $1" }, diff --git a/frontend/playwright.config.ts b/frontend/playwright.config.ts index 98fccbcb7f..6733c67536 100644 --- a/frontend/playwright.config.ts +++ b/frontend/playwright.config.ts @@ -16,6 +16,8 @@ const config: PlaywrightTestConfig = { updateSnapshots: 'all', fullyParallel: false, quiet: true, + testMatch: ['**/*.spec.ts'], + reporter: process.env.CI ? 'github' : 'list', }; export default config; diff --git a/frontend/src/api/user/getVersion.ts b/frontend/src/api/user/getVersion.ts index a65ede2f0d..0f3e7f8e83 100644 --- a/frontend/src/api/user/getVersion.ts +++ b/frontend/src/api/user/getVersion.ts @@ -1,14 +1,15 @@ import axios from 'api'; import { ErrorResponseHandler } from 'api/ErrorResponseHandler'; import { AxiosError } from 'axios'; +import { getVersion } from 'constants/api'; import { ErrorResponse, SuccessResponse } from 'types/api'; import { PayloadProps } from 'types/api/user/getVersion'; -const getVersion = async (): Promise< +const getVersionApi = async (): Promise< SuccessResponse | ErrorResponse > => { try { - const response = await axios.get(`/version`); + const response = await axios.get(`/${getVersion}`); return { statusCode: 200, @@ -21,4 +22,4 @@ const getVersion = async (): Promise< } }; -export default getVersion; +export default getVersionApi; diff --git a/frontend/src/constants/api.ts b/frontend/src/constants/api.ts new file mode 100644 index 0000000000..8ebfe3b73c --- /dev/null +++ b/frontend/src/constants/api.ts @@ -0,0 +1,3 @@ +const getVersion = 'version'; + +export { getVersion }; diff --git a/frontend/src/pages/SignUp/SignUp.tsx b/frontend/src/pages/SignUp/SignUp.tsx index d184e74a4e..9465d870c4 100644 --- a/frontend/src/pages/SignUp/SignUp.tsx +++ b/frontend/src/pages/SignUp/SignUp.tsx @@ -262,12 +262,13 @@ function SignUp({ version }: SignUpProps): JSX.Element { setState(updateValue, setConfirmPassword); }} required - id="UpdatePassword" + id="confirmPassword" /> {confirmPasswordError && ( Last Updated\"" + }, + { + "name": "AUTH_TOKEN", + "value": "authtoken" + }, + { + "name": "IS_LOGGED_IN", + "value": "true" + }, + { + "name": "REFRESH_AUTH_TOKEN", + "value": "refreshJwt" + } + ] + } + ] +} \ No newline at end of file diff --git a/frontend/tests/fixtures/api/login/200.json b/frontend/tests/fixtures/api/login/200.json new file mode 100644 index 0000000000..2ea22f87d8 --- /dev/null +++ b/frontend/tests/fixtures/api/login/200.json @@ -0,0 +1,7 @@ +{ + "accessJwt": "authtoken", + "accessJwtExpiry": 1656609177, + "refreshJwt": "refreshJwt", + "refreshJwtExpiry": 1659199377, + "userId": "34917776-514b-4b95-a4f5-1a5cc06e34b6" +} diff --git a/frontend/tests/fixtures/api/organisation/201.json b/frontend/tests/fixtures/api/organisation/201.json new file mode 100644 index 0000000000..deea4b3512 --- /dev/null +++ b/frontend/tests/fixtures/api/organisation/201.json @@ -0,0 +1,3 @@ +{ + "data": "org updated successfully" +} diff --git a/frontend/tests/fixtures/api/register/200.json b/frontend/tests/fixtures/api/register/200.json new file mode 100644 index 0000000000..6088583942 --- /dev/null +++ b/frontend/tests/fixtures/api/register/200.json @@ -0,0 +1 @@ +{ "data": "user registered successfully" } diff --git a/frontend/tests/fixtures/api/register/401.json b/frontend/tests/fixtures/api/register/401.json new file mode 100644 index 0000000000..6fd241b44c --- /dev/null +++ b/frontend/tests/fixtures/api/register/401.json @@ -0,0 +1,5 @@ +{ + "status": "error", + "errorType": "unauthorized", + "error": "You are not allowed to create an account. Please ask your admin to send an invite link" +} diff --git a/frontend/tests/fixtures/api/userId/200.json b/frontend/tests/fixtures/api/userId/200.json new file mode 100644 index 0000000000..527c60eab6 --- /dev/null +++ b/frontend/tests/fixtures/api/userId/200.json @@ -0,0 +1,11 @@ +{ + "createdAt": 1651759141, + "email": "prashant@signoz.io", + "groupId": "36261238-3214-4ae9-9ef1-661a9f7be5d0", + "id": "509fab4a-2578-4f24-8245-1b77b2d6d937", + "name": "Prashant", + "orgId": "72b4024a-3301-4d90-951e-ee071b96dba5", + "organization": "Meta", + "profilePictureURL": "", + "role": "ADMIN" +} diff --git a/frontend/tests/fixtures/common.ts b/frontend/tests/fixtures/common.ts new file mode 100644 index 0000000000..d691cae423 --- /dev/null +++ b/frontend/tests/fixtures/common.ts @@ -0,0 +1,43 @@ +import { Page } from '@playwright/test'; +import { getVersion } from 'constants/api'; + +import loginApiResponse from './api/login/200.json'; +import updateOrgResponse from './api/organisation/201.json'; +import successLoginResponse from './api/register/200.json'; +import userLoginResponse from './api/userId/200.json'; +import { version } from './constant'; + +export const waitForVersionApiSuccess = async (page: Page): Promise => { + await page.route(`**/${getVersion}`, (route) => + route.fulfill({ + status: 200, + body: JSON.stringify({ version }), + }), + ); +}; + +export const loginApi = async (page: Page): Promise => { + await Promise.all([ + page.route(`**/register`, (route) => + route.fulfill({ + status: 200, + body: JSON.stringify(successLoginResponse), + }), + ), + page.route(`**/user/${loginApiResponse.userId}`, (route) => + route.fulfill({ status: 200, body: JSON.stringify(userLoginResponse) }), + ), + page.route('**/login', (route) => + route.fulfill({ + status: 200, + body: JSON.stringify(loginApiResponse), + }), + ), + page.route(`**/org/${userLoginResponse.orgId}`, (route) => + route.fulfill({ + status: 200, + body: JSON.stringify(updateOrgResponse), + }), + ), + ]); +}; diff --git a/frontend/tests/fixtures/constant.ts b/frontend/tests/fixtures/constant.ts new file mode 100644 index 0000000000..ac20029c4a --- /dev/null +++ b/frontend/tests/fixtures/constant.ts @@ -0,0 +1,8 @@ +export const version = 'v1.0.0'; +export const validemail = 'sample@signoz.io'; +export const validName = 'Palash'; +export const validCompanyName = 'Signoz'; +export const validPassword = 'SamplePassword98@@'; + +export const getStartedButtonSelector = 'button[data-attr="signup"]'; +export const confirmPasswordSelector = '#password-confirm-error'; diff --git a/frontend/tests/login/fail.spec.ts b/frontend/tests/login/fail.spec.ts new file mode 100644 index 0000000000..5366d7240c --- /dev/null +++ b/frontend/tests/login/fail.spec.ts @@ -0,0 +1,28 @@ +import { expect, test } from '@playwright/test'; +import { getVersion } from 'constants/api'; +import ROUTES from 'constants/routes'; + +test.describe('Version API fail while loading login page', async () => { + test('Something went wrong', async ({ page, baseURL }) => { + const loginPage = `${baseURL}${ROUTES.LOGIN}`; + + const text = 'Something went wrong'; + + await page.route(`**/${getVersion}`, (route) => + route.fulfill({ + status: 500, + body: JSON.stringify({ error: text }), + }), + ); + + await page.goto(loginPage, { + waitUntil: 'networkidle', + }); + + const el = page.locator(`text=${text}`); + + expect(el).toBeVisible(); + expect(el).toHaveText(`${text}`); + expect(await el.getAttribute('disabled')).toBe(null); + }); +}); diff --git a/frontend/tests/login/index.spec.ts b/frontend/tests/login/index.spec.ts new file mode 100644 index 0000000000..ec735460ab --- /dev/null +++ b/frontend/tests/login/index.spec.ts @@ -0,0 +1,49 @@ +import { expect, test } from '@playwright/test'; +import ROUTES from 'constants/routes'; + +import { waitForVersionApiSuccess } from '../fixtures/common'; +import { version } from '../fixtures/constant'; + +test.describe('Login Page', () => { + test.beforeEach(async ({ baseURL, page }) => { + const loginPage = `${baseURL}${ROUTES.LOGIN}`; + + await waitForVersionApiSuccess(page); + + await Promise.all([page.goto(loginPage), page.waitForRequest('**/version')]); + }); + + test('Login Page text should be visible', async ({ page }) => { + const signup = 'Monitor your applications. Find what is causing issues.'; + + // Click text=Monitor your applications. Find what is causing issues. + const el = page.locator(`text=${signup}`); + + expect(el).toBeVisible(); + }); + + test('Create an account button should be present', async ({ + page, + baseURL, + }) => { + const loginPage = `${baseURL}${ROUTES.LOGIN}`; + + // find button which has text=Create an account + const button = page.locator('text=Create an account'); + + expect(button).toBeVisible(); + expect(button).toHaveText('Create an account'); + expect(await button.getAttribute('disabled')).toBe(null); + + expect(await button.isEnabled()).toBe(true); + await expect(page).toHaveURL(loginPage); + }); + + test('Version of the application when api returns 200', async ({ page }) => { + // Click text=SigNoz ${version} + const element = page.locator(`text=SigNoz ${version}`); + element.isVisible(); + const text = await element.innerText(); + expect(text).toBe(`SigNoz ${version}`); + }); +}); diff --git a/frontend/tests/service/index.spec.ts b/frontend/tests/service/index.spec.ts new file mode 100644 index 0000000000..ae708322ed --- /dev/null +++ b/frontend/tests/service/index.spec.ts @@ -0,0 +1,22 @@ +import { expect, Page, test } from '@playwright/test'; +import ROUTES from 'constants/routes'; + +import { loginApi } from '../fixtures/common'; + +let page: Page; + +test.describe('Service Page', () => { + test.beforeEach(async ({ baseURL, browser }) => { + const context = await browser.newContext({ storageState: 'tests/auth.json' }); + const newPage = await context.newPage(); + + await loginApi(newPage); + + await newPage.goto(`${baseURL}${ROUTES.APPLICATION}`); + + page = newPage; + }); + test('Serice Page is rendered', async ({ baseURL }) => { + await expect(page).toHaveURL(`${baseURL}${ROUTES.APPLICATION}`); + }); +}); diff --git a/frontend/tests/signup/index.spec.ts b/frontend/tests/signup/index.spec.ts index a7e06f4fa6..afdc98f140 100644 --- a/frontend/tests/signup/index.spec.ts +++ b/frontend/tests/signup/index.spec.ts @@ -1,17 +1,224 @@ -import { expect, test } from '@playwright/test'; +import { expect, Page, PlaywrightTestOptions, test } from '@playwright/test'; import ROUTES from 'constants/routes'; -test('Login Page', async ({ page, baseURL }) => { - const loginPage = `${baseURL}${ROUTES.LOGIN}`; +import { loginApi, waitForVersionApiSuccess } from '../fixtures/common'; +import { + confirmPasswordSelector, + getStartedButtonSelector, + validCompanyName, + validemail, + validName, + validPassword, +} from '../fixtures/constant'; - await page.goto(loginPage, { - waitUntil: 'networkidle', +const waitForSignUpPageSuccess = async ( + baseURL: PlaywrightTestOptions['baseURL'], + page: Page, +): Promise => { + const signupPage = `${baseURL}${ROUTES.SIGN_UP}`; + + await page.goto(signupPage); + + await waitForVersionApiSuccess(page); +}; + +interface FillDetailsInSignUpFormProps { + page: Page; + email: string; + name: string; + companyName: string; + password: string; + confirmPassword: string; +} + +const fillDetailsInSignUpForm = async ({ + page, + email, + name, + companyName, + password, + confirmPassword, +}: FillDetailsInSignUpFormProps): Promise => { + const emailplaceholder = '[placeholder="name\\@yourcompany\\.com"]'; + const nameplaceholder = '[placeholder="Your Name"]'; + const companyPlaceholder = '[placeholder="Your Company"]'; + const currentPasswordId = '#currentPassword'; + const confirmPasswordId = '#confirmPassword'; + + // Fill [placeholder="name\@yourcompany\.com"] + await page.locator(emailplaceholder).fill(email); + + // Fill [placeholder="Your Name"] + await page.locator(nameplaceholder).fill(name); + + // Fill [placeholder="Your Company"] + await page.locator(companyPlaceholder).fill(companyName); + + // Fill #currentPassword + await page.locator(currentPasswordId).fill(password); + + // Fill #confirmPasswordId + await page.locator(confirmPasswordId).fill(confirmPassword); +}; + +test.describe('Sign Up Page', () => { + test('When User successfull signup and logged in, he should be redirected to dashboard', async ({ + page, + baseURL, + }) => { + const loginPage = `${baseURL}${ROUTES.LOGIN}`; + + await waitForVersionApiSuccess(page); + + await Promise.all([page.goto(loginPage), page.waitForRequest('**/version')]); + + const buttonSignupButton = page.locator('text=Create an account'); + + await buttonSignupButton.click(); + + expect(page).toHaveURL(`${baseURL}${ROUTES.SIGN_UP}`); }); - const signup = 'Monitor your applications. Find what is causing issues.'; + test('Invite link validation', async ({ baseURL, page }) => { + await waitForSignUpPageSuccess(baseURL, page); + const message = + 'This will create an admin account. If you are not an admin, please ask your admin for an invite link'; - // Click text=Monitor your applications. Find what is causing issues. - const el = page.locator(`text=${signup}`); + const messageText = await page.locator(`text=${message}`).innerText(); - expect(el).toBeVisible(); + expect(messageText).toBe(message); + }); + + test('User Sign up with valid details', async ({ baseURL, page, context }) => { + await waitForSignUpPageSuccess(baseURL, page); + + const gettingStartedButton = page.locator(getStartedButtonSelector); + + expect(await gettingStartedButton.isDisabled()).toBe(true); + + await fillDetailsInSignUpForm({ + companyName: validCompanyName, + confirmPassword: validPassword, + email: validemail, + name: validName, + page, + password: validPassword, + }); + + // password validation message is not present + const locator = await page.locator(confirmPasswordSelector).isVisible(); + expect(locator).toBe(false); + + const buttonText = await gettingStartedButton.evaluate((e) => e.innerHTML); + + expect(buttonText).toMatch(/Get Started/i); + + // Getting Started button is not disabled + expect(await gettingStartedButton.isDisabled()).toBe(false); + + await loginApi(page); + + await gettingStartedButton.click(); + + await expect(page).toHaveURL(`${baseURL}${ROUTES.APPLICATION}`); + + await context.storageState({ + path: 'tests/auth.json', + }); + }); + + test('Empty name with valid details', async ({ baseURL, page }) => { + await waitForSignUpPageSuccess(baseURL, page); + + await fillDetailsInSignUpForm({ + companyName: validCompanyName, + confirmPassword: validPassword, + email: validemail, + name: '', + page, + password: validPassword, + }); + + const gettingStartedButton = page.locator(getStartedButtonSelector); + + expect(await gettingStartedButton.isDisabled()).toBe(true); + }); + + test('Empty Company name with valid details', async ({ baseURL, page }) => { + await waitForSignUpPageSuccess(baseURL, page); + + await fillDetailsInSignUpForm({ + companyName: '', + confirmPassword: validPassword, + email: validemail, + name: validName, + page, + password: validPassword, + }); + + const gettingStartedButton = page.locator(getStartedButtonSelector); + + expect(await gettingStartedButton.isDisabled()).toBe(true); + }); + + test('Empty Email with valid details', async ({ baseURL, page }) => { + await waitForSignUpPageSuccess(baseURL, page); + + await fillDetailsInSignUpForm({ + companyName: validCompanyName, + confirmPassword: validPassword, + email: '', + name: validName, + page, + password: validPassword, + }); + + const gettingStartedButton = page.locator(getStartedButtonSelector); + + expect(await gettingStartedButton.isDisabled()).toBe(true); + }); + + test('Empty Password and confirm password with valid details', async ({ + baseURL, + page, + }) => { + await waitForSignUpPageSuccess(baseURL, page); + + await fillDetailsInSignUpForm({ + companyName: validCompanyName, + confirmPassword: '', + email: validemail, + name: validName, + page, + password: '', + }); + + const gettingStartedButton = page.locator(getStartedButtonSelector); + + expect(await gettingStartedButton.isDisabled()).toBe(true); + + // password validation message is not present + const locator = await page.locator(confirmPasswordSelector).isVisible(); + expect(locator).toBe(false); + }); + + test('Miss Match Password and confirm password with valid details', async ({ + baseURL, + page, + }) => { + await waitForSignUpPageSuccess(baseURL, page); + + await fillDetailsInSignUpForm({ + companyName: validCompanyName, + confirmPassword: validPassword, + email: validemail, + name: validName, + page, + password: '', + }); + + // password validation message is not present + const locator = await page.locator(confirmPasswordSelector).isVisible(); + expect(locator).toBe(true); + }); }); diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json index ca86de66b0..92ea1e3649 100644 --- a/frontend/tsconfig.json +++ b/frontend/tsconfig.json @@ -36,6 +36,7 @@ "./commitlint.config.ts", "./webpack.config.js", "./webpack.config.prod.js", - "./jest.setup.ts" + "./jest.setup.ts", + "./tests/**.ts", ] }