Skip to content

first draft of Books - Basket scenarios #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions docs/test_plan.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
Generally I would like to speak with the PO about the product, with the Team about technology and then before everything
we should describe scope and out-of-scope of automation.

During this task i discovered bellow activities to be done.

INCREASE REGRESSION COVERAGE:
UI
1. Cover critical functionalities - related with MVP for current project stage , where all functionalities should be
working and stable to deliver value:
- registration and login flow
- payment flow - needs further investigation and analysis
- editing flow ( in iterations, this part requires additional functionalities mapping to describe more detialed view)
- Customer Service
2. MVP - 1 happy path including selling per one product ( book, cards etc. )
3. User Profile:
- Settings - editing
- Saved Projects
- Online PhotoBooks
- FAQs
- Orders ( may require further functionalities mapping and exploration )
4. Blog
5. Cover more edge cases and other functionalities of products
LOAD - if it is not done yet, then it is huge risk to not do it ASAP
it requires to perform load tests on the backend side
- evaluate available technology
- create a POC of evaluated tech
- start analysing backend metrics ( observability ) and based on findings design covering load scenarios
- extend load scenarios if needed
SECURITY
the minimum is:
1. Static Analysis
2. Dynamic analysis
SAST sometimes can be done through the config of CI/CD ( f.ex. gitlab ci scanners )
may require evaluation of new technology ( f.ex.: Sonar Cloud , Snyk )

IMPROVEMENTS
1. critical improvements :
- create user creation factory , common for all test cases with proper environment config - trough API
( including countries grouping, permissions, feature flags etc. )
2. implement configuration allowing for multi-country testing ( common scenarios, country dedicated etc. )
3. Observability - start observing the test runs , cycles to spot any trends ( flaky, short, longest etc. ):
- may require to evaluate dedicated technology ( f.ex.: allure )
2 changes: 1 addition & 1 deletion layouts.ts → helpers/photosGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { writeFile, mkdir } from "fs/promises";
import type { Locator, Page } from "@playwright/test";
import type { Page } from "@playwright/test";
import { toPng } from "jdenticon";

export class PhotoSelectorLayout {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,8 @@
"dependencies": {
"@playwright/test": "1.17.2",
"jdenticon": "3.1.1"
},
"devDependencies": {
"@faker-js/faker": "^7.6.0"
}
}
4 changes: 3 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ const config: PlaywrightTestConfig = {
// https://playwright.dev/docs/test-configuration
timeout: 50000,
use: {
//headless: false,
headless: true,
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "on-first-retry"
},
};

Expand Down
49 changes: 49 additions & 0 deletions selectors/editor-step-3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import {expect, Locator, Page} from '@playwright/test';

export class EditorStep3 {
readonly addToBasketBtn: Locator
readonly stepIndicator: Locator
readonly dataPrintSurface: Locator
readonly editingDialogueWindow: Locator
readonly editingDialogueCancel: Locator
readonly editingDialogueConfirm: Locator
readonly editingDialogueTitle: Locator
readonly editingDialogueTitleErr: Locator
readonly campaignForm: Locator
readonly finaliseDialogueWindow: Locator
readonly finaliseDialogueContinue: Locator
readonly finaliseDialogueBack: Locator



constructor(private page: Page) {
this.addToBasketBtn = page.locator("[data-tam=add-to-basket]");
this.stepIndicator = page.locator("[data-tam=step-indicator]");
this.dataPrintSurface= page.locator("[data-tam=print-surface]");

this.editingDialogueWindow = page.locator("[data-testid=dialog-window]");
this.editingDialogueCancel = page.locator("[data-tam=cancel-cover-edit]");
//TODO bellow element has overlay and should be fixed in UI, because some parents layers are hiding given one with
// data-tam attribute. Cannot be clicked directly
this.editingDialogueConfirm = page.locator("[data-tam=confirm-cover-edit]");
//TODO add data-testid for bellow selector
this.editingDialogueTitle = page.locator("[placeholder=\"Enter a title\"]:required");
this.editingDialogueTitleErr = page.locator(".status-container");
this.campaignForm = page.locator("form[campaign-form]");
this.finaliseDialogueContinue = page.locator("[data-testid=dialog-button][data-tam=dismiss-warning]");
this.finaliseDialogueBack = page.locator("[data-testid=dialog-button][data-tam=resolve-warning]");
this.finaliseDialogueWindow = page.locator("[data-testid=dialog-window]");

}

async clickAddToBasketBtn() {
await this.addToBasketBtn.click();
await expect(this.editingDialogueWindow).toBeVisible();
}

async clickConfirmEdtDialogue() {
await this.editingDialogueConfirm.click();
}


}
15 changes: 15 additions & 0 deletions selectors/product-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import {Locator, Page} from '@playwright/test';

export class ProductConfig {
readonly yourPhotoBookTitle: Locator
readonly productLoading: Locator



constructor(private page: Page) {
//bellow CSS selector is anitpatern and should be replaced with data-testid
this.yourPhotoBookTitle = page.locator("h1.container-title");
this.productLoading = page.locator("[data-testid=loading]");

}
}
19 changes: 19 additions & 0 deletions selectors/user-registration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Locator, Page} from '@playwright/test';

export class UserRegistration {
readonly emailInput: Locator
readonly passwordInput: Locator
readonly confirmPassInout: Locator
readonly registrationBtn: Locator


constructor(private page: Page) {
// bellow locators sounds like cypress dedicated
// should be aligned and greed on one schema , or it is 3rd party responsibility
this.emailInput = page.locator("[data-cy=EmailInput]");
this.passwordInput = page.locator("[data-cy=PasswordInput]")
this.confirmPassInout = page.locator("[data-cy=ConfirmPasswordInput]")
this.registrationBtn = page.locator("[data-cy=RegisterButton]")
}

}
59 changes: 41 additions & 18 deletions setup.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import { test as base } from "@playwright/test";
import {test as base} from '@playwright/test';

export type TestEnvironment = "test" | "acceptance" | "production";
export type Channel = "bonusprint.co.uk";
export type ArticleType = "HardCoverPhotoBook";
enum EnvTypes {
Test = 'test',
Acceptance = 'acceptance',
Production = 'production',
}

export type TestEnvironment = EnvTypes;
export type Channel = 'bonusprint.co.uk';
export type ArticleType = 'HardCoverPhotoBook';
export type TestConfig = {
testEnvironment: TestEnvironment;
getInstantEditorUrl: (channel: Channel, articleType: ArticleType, papId: string) => string;
getLoginRegisterUrl: (path: string) => string;
};

export const testEnvironment: TestEnvironment = getAndValidateEnvironment(process.env.TEST_ENV);

export const test = base.extend<TestConfig>({
testEnvironment: testEnvironment,
getInstantEditorUrl: async ({ testEnvironment }, use) => {
getInstantEditorUrl: async ({testEnvironment}, use) => {
await use(getInstantEditorUrl.bind(this, testEnvironment));
},
getLoginRegisterUrl: async ({testEnvironment}, use) => {
await use(getLoginRegisterUrl.bind(this, testEnvironment));
},
});

/**
Expand All @@ -25,13 +35,13 @@ export const test = base.extend<TestConfig>({
* @return option
*/
function getAndValidateEnvironment(option?: string): TestEnvironment {
option = String(option || "").toLowerCase();
option = String(option || '').toLowerCase();
switch (option) {
case "":
return "test";
case "test":
case "acceptance":
case "production":
case '':
return EnvTypes.Test;
case EnvTypes.Test:
case EnvTypes.Acceptance:
case EnvTypes.Production:
return option as TestEnvironment;
default:
throw Error(`Unknown environment: ${option}.`);
Expand All @@ -42,25 +52,38 @@ function getAndValidateEnvironment(option?: string): TestEnvironment {
* @return {string} href
*/
function getInstantEditorUrl(env: TestEnvironment, channel: Channel, articleType: ArticleType, papId: string): string {
const url = new URL("http://localhost/index.html");
const url = new URL('http://localhost/index.html');
switch (env) {
case "test":
case EnvTypes.Test:
url.hostname = `t-dtap.editor.${channel}`;
url.pathname = `/instant` + url.pathname;
break;
case "acceptance":
case EnvTypes.Acceptance:
url.hostname = `a-dtap.editor.${channel}`;
url.pathname = `/instant` + url.pathname;
break;
case "production":
case EnvTypes.Production:
url.hostname = `editor.${channel}`;
url.pathname = `/instant` + url.pathname;
break;
default:
throw Error(`Unknown environment: ${env}.`);
}
url.searchParams.set("articleType", articleType.toLocaleLowerCase());
url.searchParams.set("papId", papId.toUpperCase());
url.searchParams.set("testExecution", "true");
url.searchParams.set('articleType', articleType.toLocaleLowerCase());
url.searchParams.set('papId', papId.toUpperCase());
url.searchParams.set('testExecution', 'true');
return url.href;
}

function getLoginRegisterUrl(env: TestEnvironment, path: string) {
// const url = new URL("http://localhost");
let fullHostName;
switch (env) {
case EnvTypes.Test:
fullHostName = 'https://t-dtap.login.albelli.com/' + path;
break;
default:
fullHostName = 'https://t-dtap.login.albelli.com/register';
}
return fullHostName;
}
42 changes: 42 additions & 0 deletions test/auth/account/register/user-registration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {test} from '../../../../setup';
import {UserRegistration} from '../../../../selectors/user-registration';
import {faker} from '@faker-js/faker';
import {expect} from '@playwright/test';


test.describe("User registration", () => {
test.beforeEach(async ({ page,getLoginRegisterUrl }) => {

const registerUrl = getLoginRegisterUrl("register");
await page.goto(registerUrl);

})
test("Given user wants to register himself", async ({ page }) => {
const userRegistrationSelectors = new UserRegistration(page)

await test.step("When type valid email address", async () => {
await userRegistrationSelectors.emailInput.type(faker.internet.email())
});

await test.step("And type his valid password", async () => {
await userRegistrationSelectors.passwordInput.type('Password!02')

});
await test.step("And confirm pass", async () => {
await userRegistrationSelectors.confirmPassInout.type('Password!02')

});

await test.step("Then user is registered", async () => {
await page.route('https://t-dtap.login.albelli.com/api/register', (route) => {
route.fulfill({
status: 200
});
});
})

await test.step("And submit", async () => {
await userRegistrationSelectors.registrationBtn.click()
});
});
});
Loading