testsuite/playwright: copys files to storj repo (#5648)

* testsuite/playwright: copys files to storj repo
This PR copies all the playwright ui tests to the storj repository.

* CI: Jenkinsfile.ui
Jenkinsfile.ui builds all necessary items for running storj binaries, installs all items for playwright ui tests and executes the test. Note that we aren't running regular unit tests and we are not linting the code as this is occuring in the public instance. Furthermore, we still need to implement the starting of storj services as well as typescript linters for the playwright tests.

* testsuite: playwright ui tests
This PR copies all playwright ui tests files from the qa repo.

* testsuite: playwright ui tests
This PR copies all playwright ui tests files from the qa repo.
This commit is contained in:
Antonio Franco (He/Him) 2023-03-09 11:59:45 -05:00 committed by GitHub
parent 0c177ef91f
commit 3613bfe1af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 3129 additions and 0 deletions

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {test as baseTest} from '@playwright/test';
import {LoginPage} from '@pages/LoginPage';
import {DashboardPage} from '@pages/DashboardPage';
import {NavigationMenu} from '@pages/NavigationMenu';
import {BucketsPage} from '@pages/BucketsPage';
import {SignupPage} from "@pages/SignupPage";
const test = baseTest.extend<{
loginPage: LoginPage;
dashboardPage: DashboardPage;
navigationMenu: NavigationMenu;
bucketsPage: BucketsPage;
signupPage: SignupPage;
}>({
loginPage: async ({page}, use) => {
await use(new LoginPage(page));
},
dashboardPage: async ({page}, use) => {
await use(new DashboardPage(page));
},
navigationMenu: async ({page}, use) => {
await use(new NavigationMenu(page));
},
bucketsPage: async ({page}, use) => {
await use(new BucketsPage(page));
},
signupPage: async ({page}, use) => {
await use(new SignupPage(page));
}
});
export default test;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,29 @@
{
"name": "sand",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"allure:generate": "npx allure generate ./allure-results --clean",
"allure:open": "npx allure open ./allure-report",
"allure:serve": "npx allure serve",
"posttest": "npm run allure:generate",
"test": "npx playwright test",
"test-dev": "npx playwright test ./tests/bucket.test.ts --headed --project=chromium --trace on"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@playwright/test": "^1.29.2",
"playwright-slack-report": "^1.0.19",
"mocha-multi-reporters": "^1.5.1",
"allure-commandline": "^2.20.1",
"allure-playwright": "^2.0.0-beta.21",
"rimraf": "^4.0.4"
},
"dependencies": {
"@slack/socket-mode": "^1.3.2",
"@slack/web-api": "^6.8.0"
}
}

View File

@ -0,0 +1,35 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export class BucketsPageObjects {
protected static ENCRYPTION_PASSPHRASE_XPATH = `//input[@id='Encryption Passphrase']`;
protected static CONTINUE_BUTTON_PASSPHRASE_MODAL_XPATH = `//span[contains(text(),'Continue ->')]`;
protected static DOWNLOAD_BUTTON_XPATH = `//span[contains(text(),'Download')]`;
protected static SHARE_BUTTON_XPATH = ` //span[contains(text(),'Share')]`;
protected static DOWNLOAD_NOTIFICATION = `//p[contains(text(),'Do not share download link with other people. If you want to share this data bet')]`;
protected static OBJECT_MAP_TEXT_XPATH = `//div[contains(text(),'Nodes storing this file')]`;
protected static OBJECT_MAP_IMAGE_XPATH = `//*[contains(@class, 'object-map')]`;
protected static COPY_LINK_BUTTON_XPATH = `//span[contains(text(),'Copy Link')]`;
protected static COPIED_BUTTON_XPATH = `//span[contains(text(),'Copied!')]`;
protected static CLOSE_MODAL_BUTTON_XPATH = `.mask__wrapper__container__close`;
protected static NEW_FOLDER_BUTTON_XPATH = `//*[contains(text(),'New Folder')]`;
protected static NEW_FOLDER_NAME_FIELD_XPATH = `//input[@id='Folder name']`;
protected static CREATE_FOLDER_BUTTON_XPATH = `//span[contains(text(),'Create Folder')]`;
protected static DELETE_BUTTON_XPATH = `//p[contains(text(),'Delete')]`;
protected static YES_BUTTON_XPATH = `//*[contains(@class, 'delete-confirmation__options__item yes')]`;
protected static VIEW_BUCKET_DETAILS_BUTTON_CSS = `.bucket-settings-nav__dropdown__item`;
protected static BUCKET_SETTINGS_BUTTON_CSS = `.bucket-settings-nav`;
protected static SHARE_BUCKET_BUTTON_XPATH = '//p[contains(text(),\'Share bucket\')]';
protected static COPY_BUTTON_SHARE_BUCKET_MODAL_XPATH = `//span[contains(text(),'Copy')]`;
// Create new bucket flow
protected static NEW_BUCKET_BUTTON_XPATH = `//p[contains(text(),'New Bucket')]`;
protected static BUCKET_NAME_INPUT_FIELD_XPATH = `//input[@id='Bucket Name']`;
protected static CONTINUE_BUTTON_CREATE_BUCKET_FLOW_XPATH = `//span[contains(text(),'Create bucket')]`;
protected static ENTER_PASSPHRASE_RADIO_BUTTON_XPATH = `//h4[contains(text(),'Enter passphrase')]`;
protected static PASSPHRASE_INPUT_NEW_BUCKET_XPATH = `//input[@id='Your Passphrase']`;
protected static CHECKMARK_ENTER_PASSPHRASE_XPATH = `//label[contains(text(),'I understand, and I have saved the passphrase.')]`;
protected static BUCKET_NAME_DELETE_BUCKET_MODAL_XPATH = `//input[@id='Bucket Name']`;
protected static CONFIRM_DELETE_BUTTON_XPATH = `//span[contains(text(),'Confirm Delete Bucket')]`
}

View File

@ -0,0 +1,10 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export class DashboardPageObjects {
protected static WELCOME_TEXT_LOCATOR = `//h2[contains(text(),'Project Stats')]`;
protected static ENTER_PASSPHRASE_RADIO_BUTTON_XPATH = `//*[contains(text(),'Enter passphrase')]`;
protected static CONTINUE_PASSPHRASE_BUTTON_XPATH = `//span[contains(text(),'Continue ->')]`;
protected static PASSPHRASE_INPUT_XPATH = `//input[@id='Encryption Passphrase']`;
protected static CHECKMARK_ENTER_PASSPHRASE_XPATH = `//h2[contains(text(),'Yes I understand and saved the passphrase.')]`
}

View File

@ -0,0 +1,8 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export class LoginPageObjects {
protected static EMAIL_EDITBOX_ID = `//input[@id='Email Address']`;
protected static PASSWORD_EDITBOX_ID = `//input[@id='Password']`;
protected static SIGN_IN_BUTTON_ID = `//span[contains(text(),'Sign In')]`;
}

View File

@ -0,0 +1,6 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export class NavigationMenuObject {
protected static BUCKETS_XPATH = `Buckets`;
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export class SignupPageObjects {
// SIGNUP
protected static INPUT_NAME_XPATH = `//input[@id='Full Name']`;
protected static INPUT_EMAIL_XPATH = `//input[@id='Email Address']`;
protected static INPUT_PASSWORD_XPATH = `//input[@id='Password']`;
protected static INPUT_RETYPE_PASSWORD_XPATH = `//input[@id='Retype Password']`;
protected static TOS_CHECKMARK_BUTTON_XPATH = `.checkmark-container`;
// SIGNUP SUCCESS PAGE
protected static SIGNUP_SUCCESS_MESSAGE_XPATH = `//h2[contains(text(),"You\'re almost there!")]`;
protected static GOTO_LOGIN_PAGE_BUTTON_XPATH = `//a[contains(text(),'Go to Login page')]`;
// IX BRANDED SIGNUP
protected static CREATE_ACCOUNT_BUTTON_XPATH = '//span[contains(text(),\'Create an iX-Storj Account\')]';
protected static IX_BRANDED_HEADER_TEXT_XPATH = '//h1[contains(text(),\'Globally Distributed Storage for TrueNAS\')]';
protected static IX_BRANDED_SUBHEADER_TEXT_XPATH = '//p[contains(text(),\'iX and Storj have partnered to offer a secure, hig\')]';
// BUSINESS TAB
protected static IX_BRANDED_BUSINESS_BUTTON_XPATH = `//li[contains(text(),'Business')]`;
protected static COMPANY_NAME_INPUT_XPATH = `//input[@id='Company Name']`;
protected static POSITION_INPUT_XPATH = `//input[@id='Position']`;
}

View File

@ -0,0 +1,178 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {BucketsPageObjects} from "@objects/BucketsPageObjects";
import type {Page} from '@playwright/test';
import {expect} from "@playwright/test";
export class BucketsPage extends BucketsPageObjects {
readonly page: Page;
constructor(page: Page) {
super();
this.page = page;
}
async openBucketByName(name: string): Promise<void> {
await this.page.getByText(name).click();
}
async enterPassphrase(phrase: string): Promise<void> {
await this.page.locator(BucketsPageObjects.ENCRYPTION_PASSPHRASE_XPATH).fill(phrase)
}
async clickContinueConfirmPassphrase() {
await this.page.locator(BucketsPageObjects.CONTINUE_BUTTON_PASSPHRASE_MODAL_XPATH).click();
}
async downloadFileByName(name: string): Promise<void> {
const uiTestFile = this.page.getByText(name);
await expect(uiTestFile).toBeVisible();
await uiTestFile.click();
await Promise.all([
this.page.waitForEvent('download'),
this.page.locator(BucketsPageObjects.DOWNLOAD_BUTTON_XPATH).click()
]);
await expect(this.page.locator(BucketsPageObjects.DOWNLOAD_NOTIFICATION)).toBeVisible();
}
async clickShareButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.SHARE_BUTTON_XPATH).click();
}
async clickCopyLinkButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.COPY_LINK_BUTTON_XPATH).click();
const textValue = await this.page.textContent(BucketsPageObjects.COPIED_BUTTON_XPATH);
expect(textValue.trim()).toBe(`Copied!`);
}
async clickCopyButtonShareBucketModal(): Promise<void> {
await this.page.locator(BucketsPageObjects.COPY_BUTTON_SHARE_BUCKET_MODAL_XPATH).click();
await this.page.locator('span').filter({hasText: 'Copied'}).isVisible();
await expect(this.page.getByText('Link copied successfully.')).toBeVisible()
}
async verifyObjectMapIsVisible(): Promise<void> {
await this.page.locator(BucketsPageObjects.OBJECT_MAP_TEXT_XPATH).isVisible();
await this.page.locator(BucketsPageObjects.OBJECT_MAP_IMAGE_XPATH).isVisible();
}
async verifyImagePreviewIsVisible(): Promise<void> {
await this.page.getByRole('img', {name: 'preview'}).isVisible()
}
async closeModal(): Promise<void> {
await this.page.locator(BucketsPageObjects.CLOSE_MODAL_BUTTON_XPATH).click();
}
async openFileDropdownByName(name: string): Promise<void> {
const row = await this.page.waitForSelector('*css=tr >> text=' + name);
const button = await row.$('th:nth-child(4)');
await button.click();
}
async clickDeleteFileButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.DELETE_BUTTON_XPATH).click();
}
async clickNewFolderButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.NEW_FOLDER_BUTTON_XPATH).click();
}
async createNewFolder(name: string): Promise<void> {
await this.clickNewFolderButton();
await this.page.locator(BucketsPageObjects.NEW_FOLDER_NAME_FIELD_XPATH).fill(name);
await this.page.locator(BucketsPageObjects.CREATE_FOLDER_BUTTON_XPATH).click();
}
async openFileByName(name: string): Promise<void> {
await this.page.getByText(name).click();
await this.page.locator(`//p[contains(text(),'${name}')]`).isVisible()
}
async openBucketSettings(): Promise<void> {
await this.page.locator(BucketsPageObjects.BUCKET_SETTINGS_BUTTON_CSS).click();
}
async clickViewBucketDetails(): Promise<void> {
await this.page.locator(BucketsPageObjects.VIEW_BUCKET_DETAILS_BUTTON_CSS).first().click();
}
async deleteFileByName(name: string): Promise<void> {
await this.openFileDropdownByName(name);
await this.clickDeleteFileButton();
await this.page.locator(BucketsPageObjects.YES_BUTTON_XPATH).click();
await this.page.getByText(name).waitFor({state: "hidden"});
}
async dragAndDropFile(name: string, format: string): Promise<void> {
await this.page.setInputFiles("input[type='file']", {
name: name,
mimeType: format,
buffer: Buffer.from('Test,T')
});
await expect(await this.page.getByText(name)).toBeVisible();
}
async dragAndDropFolder(folder: string, filename: string, format: string): Promise<void> {
await this.page.setInputFiles("input[type='file']", {
name: folder + "/" + filename,
mimeType: format,
buffer: Buffer.from('Test,T')
});
await expect(await this.page.getByText(folder)).toBeVisible();
}
async verifyDetails(name: string): Promise<void> {
await this.page.getByRole('cell', {name: name}).isVisible()
}
async clickShareBucketButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.SHARE_BUCKET_BUTTON_XPATH).click();
}
async clickNewBucketButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.NEW_BUCKET_BUTTON_XPATH).click();
}
async enterNewBucketName(name: string): Promise<void> {
await this.page.locator(BucketsPageObjects.BUCKET_NAME_INPUT_FIELD_XPATH).fill(name);
}
async clickContinueCreateBucket(): Promise<void> {
await this.page.locator(BucketsPageObjects.CONTINUE_BUTTON_CREATE_BUCKET_FLOW_XPATH).click();
}
async clickEnterPassphraseRadioButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.ENTER_PASSPHRASE_RADIO_BUTTON_XPATH).click();
}
async enterNewBucketPassphrase(passphrase: string): Promise<void> {
await this.page.locator(BucketsPageObjects.PASSPHRASE_INPUT_NEW_BUCKET_XPATH).fill(passphrase);
}
async clickConfirmCheckmark(): Promise<void> {
await this.page.locator(BucketsPageObjects.CHECKMARK_ENTER_PASSPHRASE_XPATH).click();
}
async openBucketDropdownByName(name: string): Promise<void> {
const row = await this.page.waitForSelector('*css=tr >> text=' + name);
const button = await row.$('th:nth-child(7)');
await button.click();
}
async clickDeleteBucketButton(): Promise<void> {
await this.page.locator('//p[contains(text(),\'Delete Bucket\')]').click();
}
async enterBucketNameDeleteBucket(name: string): Promise<void> {
await this.page.locator(BucketsPageObjects.BUCKET_NAME_DELETE_BUCKET_MODAL_XPATH).fill(name);
}
async clickConfirmDeleteButton(): Promise<void> {
await this.page.locator(BucketsPageObjects.CONFIRM_DELETE_BUTTON_XPATH).click();
}
}

View File

@ -0,0 +1,46 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {DashboardPageObjects} from "@objects/DashboardPageObjects";
import type {Page} from '@playwright/test';
import {expect} from '@playwright/test';
export class DashboardPage extends DashboardPageObjects {
readonly page: Page;
constructor(page: Page) {
super();
this.page = page;
}
async verifyWelcomeMessage(): Promise<void> {
await expect(this.page.locator(DashboardPageObjects.WELCOME_TEXT_LOCATOR)).toBeVisible();
}
async clickEnterPassphraseRadioButton(): Promise<void> {
await this.page.locator(DashboardPageObjects.ENTER_PASSPHRASE_RADIO_BUTTON_XPATH).click();
}
async clickContinuePassphraseButton(): Promise<void> {
await this.page.locator(DashboardPageObjects.CONTINUE_PASSPHRASE_BUTTON_XPATH).click();
}
async enterPassphrase(passphrase: string): Promise<void> {
await this.page.locator(DashboardPageObjects.PASSPHRASE_INPUT_XPATH).fill(passphrase);
}
async clickConfirmCheckmark(): Promise<void> {
await this.page.locator(DashboardPageObjects.CHECKMARK_ENTER_PASSPHRASE_XPATH).click();
}
async enterOwnPassphraseModal(passphrase: string): Promise<void> {
await this.clickEnterPassphraseRadioButton();
await this.clickContinuePassphraseButton();
await this.enterPassphrase(passphrase);
await this.clickConfirmCheckmark();
await this.clickContinuePassphraseButton();
await this.clickContinuePassphraseButton();
}
}

View File

@ -0,0 +1,26 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {LoginPageObjects} from "@objects/LoginPageObjects";
import type {Page} from '@playwright/test';
import {testConfig} from '../../testConfig';
export class LoginPage extends LoginPageObjects {
readonly page: Page;
constructor(page: Page) {
super();
this.page = page;
}
async navigateToURL(): Promise<void> {
await this.page.goto(testConfig.host);
}
async loginToApplication(): Promise<void> {
await this.page.locator(LoginPageObjects.EMAIL_EDITBOX_ID).fill(testConfig.username);
await this.page.locator(LoginPageObjects.PASSWORD_EDITBOX_ID).fill(testConfig.password);
await this.page.locator(LoginPageObjects.SIGN_IN_BUTTON_ID).click();
}
}

View File

@ -0,0 +1,20 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {NavigationMenuObject} from "@objects/NavigationMenuObject";
import type {Page} from '@playwright/test';
export class NavigationMenu extends NavigationMenuObject {
readonly page: Page;
constructor(page: Page) {
super();
this.page = page;
}
async clickOnBuckets(): Promise<void> {
await this.page.getByRole('link', {name: NavigationMenuObject.BUCKETS_XPATH}).click();
}
}

View File

@ -0,0 +1,68 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {SignupPageObjects} from "@objects/SignupPageObjects";
import type {Page} from '@playwright/test';
import {expect} from "@playwright/test";
import {testConfig} from "../../testConfig";
export class SignupPage extends SignupPageObjects {
readonly page: Page;
constructor(page: Page) {
super();
this.page = page;
}
async navigateToPartnerSignup(): Promise<void> {
await this.page.goto(testConfig.host +'/signup?partner=ix-storj-1');
}
async clickOnBusinessButton(): Promise<void> {
await this.page.locator(SignupPageObjects.IX_BRANDED_BUSINESS_BUTTON_XPATH).click()
}
async signupApplicationPersonal(name: string, email: string, password: string): Promise<void> {
await this.page.locator(SignupPageObjects.INPUT_NAME_XPATH).fill(name);
await this.page.locator(SignupPageObjects.INPUT_EMAIL_XPATH).fill(email);
await this.page.locator(SignupPageObjects.INPUT_PASSWORD_XPATH).fill(password);
await this.page.locator(SignupPageObjects.INPUT_RETYPE_PASSWORD_XPATH).fill(password);
await this.clickOnEveryCheckmark();
await this.page.locator(SignupPageObjects.CREATE_ACCOUNT_BUTTON_XPATH).click();
}
async clickOnEveryCheckmark(): Promise<void> {
const checkmarks = await this.page.$$(SignupPageObjects.TOS_CHECKMARK_BUTTON_XPATH);
for (const checkmark of checkmarks) {
await checkmark.click({timeout: 8000});
}
}
async signupApplicationBusiness(name: string, email: string, password: string, company: string, position: string): Promise<void> {
await this.clickOnBusinessButton();
await this.page.locator(SignupPageObjects.COMPANY_NAME_INPUT_XPATH).fill(company);
await this.page.locator(SignupPageObjects.POSITION_INPUT_XPATH).fill(position);
await this.signupApplicationPersonal(name, email, password);
}
async verifySuccessMessage(): Promise<void> {
await expect(this.page.locator(SignupPageObjects.SIGNUP_SUCCESS_MESSAGE_XPATH)).toBeVisible();
}
async clickOnGotoLoginPage(): Promise<void> {
await this.page.locator(SignupPageObjects.GOTO_LOGIN_PAGE_BUTTON_XPATH).click();
}
async verifyIXBrandedHeader(): Promise<void> {
await expect(this.page.locator(SignupPageObjects.IX_BRANDED_HEADER_TEXT_XPATH)).toBeVisible();
}
async verifyIXBrandedSubHeader(): Promise<void> {
await expect(this.page.locator(SignupPageObjects.IX_BRANDED_SUBHEADER_TEXT_XPATH)).toBeVisible();
}
}

View File

@ -0,0 +1,107 @@
// @ts-ignore
import os from 'node:os';
import generateCustomLayoutSimpleMeta from './slackReporter';
import { PlaywrightTestConfig, devices} from '@playwright/test';
// require('dotenv').config();
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
// Potentially interesting metadata to append into the test report might help with debugging
const metadata: Record<string, string> = {
cpu: os.arch(),
memory: `${os.totalmem() / (1024 ** 2)} MB`,
hostname: os.hostname(),
system: os.type(),
kernel: os.version(),
};
// Match pixel comparison at least 95 % to avoid flaky tests but ensure enough confidence
const threshold = 0.95;
const config: PlaywrightTestConfig = {
testDir: './tests', /* directory where tests are located. */
timeout: 30 * 1000, /* Maximum time one test can run for. */
expect: {
timeout: 4000, /* Maximum time expect() should wait for the condition to be met. */
toMatchSnapshot: {threshold}, /* only require the screenshots to be the same within a certain threshold */
},
fullyParallel: false, /* Run tests in files in parallel */
retries: process.env.CI ? 1 : 0, /* Retry on CI only */
workers: process.env.CI ? 1 : undefined, /* Opt out of parallel tests on CI. */
reporter: [
[
"./node_modules/playwright-slack-report/dist/src/SlackReporter.js",
{
channels: ["#team-integrations-console-alerts", "team-qa-github"], // provide one or more Slack channels
sendResults: "always", // "always" , "on-failure", "off"
},
],
["dot"],
["list"],
['line'],
['allure-playwright']
],
use: { /* Shared settings for all the projects below. */
actionTimeout: 0, /* Maximum time each action can take. */
baseURL: 'http://nightly.storj.rodeo:10000/', /* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:10000',
// headless: process.env.CI ? false : true, /* Starts the UI tests in headed mode, so we can watch execution in development */
ignoreHTTPSErrors: true, /* suppress the errors relative to serving web data */
trace: 'on-first-retry', /* Collect trace when retrying the failed test. */
screenshot: 'only-on-failure',
launchOptions: {
slowMo: process.env.CI ? 0 : 0,
},
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
},
{
name: 'safari',
use: {
...devices['Desktop Safari'],
},
},
{
name: 'Edge',
...devices['Desktop Edge'],
},
/* Test against mobile viewports. */
{
name: 'Android',
use: {
...devices['Pixel 5'],
},
},
{
name: 'iPhone(13)',
use: {
...devices['iPhone 13'],
},
},
],
/* Folder for test artifacts such as screenshots, videos, traces, etc. */
outputDir: 'test-results/',
};
export default config;

View File

@ -0,0 +1,36 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import {Block, KnownBlock} from "@slack/web-api";
import {SummaryResults} from "playwright-slack-report/dist/src";
export default function generateCustomLayoutSimpleMeta(
summaryResults: SummaryResults,
): Array<Block | KnownBlock> {
const meta: { type: string; text: { type: string; text: string; }; }[] = [];
if (summaryResults.meta) {
for (let i = 0; i < summaryResults.meta.length; i += 1) {
const {key, value} = summaryResults.meta[i];
meta.push({
type: 'section',
text: {
type: 'mrkdwn',
text: `\n*${key}* :\t${value}`,
},
});
}
}
return [
{
type: 'section',
text: {
type: 'mrkdwn',
text:
summaryResults.failed === 0
? ':tada: All tests passed!'
: `😭${summaryResults.failed} failure(s) out of ${summaryResults.tests.length} tests`,
},
},
...meta,
];
}

View File

@ -0,0 +1,11 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
export const testConfig = {
host: `https://localhost`,
port: `:10001`,
username: `test@storj.io`,
password: `123a123`,
waitForElement: 120000,
};

View File

@ -0,0 +1,18 @@
{
"compilerOptions": {
"baseUrl": ".",
/* Specify the base directory to resolve non-relative module names. */
"paths": {
/* Specify a set of entries that re-map imports to additional lookup locations. */
"@pages/*": [
"pageFactory/pageRepository/*"
],
"@objects/*": [
"pageFactory/objectRepository/*"
],
"@lib/*": [
"lib/*"
]
}
}
}

View File

@ -0,0 +1,15 @@
// Copyright (C) 2023 Storj Labs, Inc.
// See LICENSE for copying information.
import type {Page, TestInfo} from '@playwright/test';
export async function screenshotOnFailure({page}: { page: Page }, testInfo: TestInfo) {
if (testInfo.status !== testInfo.expectedStatus) {
// Get a unique place for the screenshot.
const screenshotPath = testInfo.outputPath(`failure.png`);
// Add it to the report.
testInfo.attachments.push({name: 'screenshot', path: screenshotPath, contentType: 'image/png'});
// Take the screenshot itself.
await page.screenshot({path: screenshotPath, timeout: 5000});
}
}