I am facing one issue related to global setup and teardown approach :
--> i have to run mutliple spec files , which are in different folders under test and wanted to login once and logout once all the spec files are executed.
-> So i have created global-setup.ts file and global-teardown.ts file for storing the state and once everything is done, teardown will logout.
--> i have given tags in the specific tests and created a config file where i have used it and created one test-runner file where these test cases will run
--> Also setup is done in playwright.config file
==> Now the problem statement is : when i am running the command , login is done and state is stored by global-setup and first test cases gets executed , but for the 2nd testcase login is happening again , if i comment the global-setup and global-teardown in playwright config , all the testcases are running as expected, because storage state is maintained , but this is not the ideal scenario, because when we run it in CI/CD problem will arise again , i am sharing code of all the files, please shared to expert advice (cant share the code on git , because this is office project) [will also share the project structure ]
global-setup.ts file :import { Browser, chromium, FullConfig } from '@playwright/test';
import { LoginPage } from '../../app/pages/login.page';
import * as fs from 'fs';
import { saveStorageState } from '../../../utils/utils';
export let browserConnection: Browser;
export let wsEndPoint: string;
async function globalSetup(config: FullConfig) {
const server = await chromium.launchServer({ headless: false });
wsEndPoint = server.wsEndpoint();
const browser = await chromium.connect(wsEndPoint);
browserConnection = browser;
const context = await browser.newContext();
const page = await context.newPage();
const loginPage = new LoginPage(page, context);
await loginPage.login();
//global.__LOGIN_PAGE__ = loginPage;
//await new Promise((resolve) => setTimeout(resolve, 3000));
await saveStorageState(context, page);
await context.close();
//await browser.close();
}
export default globalSetup;
global-terdown.ts :
import { FullConfig } from '@playwright/test';
import { chromium } from '@playwright/test';
import { LoginPage } from '../../app/pages/login.page';
import { loadStorageState } from '../../../utils/utils';
import { wsEndPoint } from './global-setup';
async function globalTeardown(config: FullConfig) {
const browser = await chromium.connect(wsEndPoint);
const context = await browser.newContext({ storageState: 'localStorageState.json' });
//const openPages = context.pages();
// console.log(Number of open tabs: ${openPages.length}
);
// Close all open pages in the context
// for (const page of openPages) {
// await page.close();
// }
const origin = await loadStorageState(context);
const page = await context.newPage();
await page.goto(origin);
const loginPage = new LoginPage(page, context);
await loginPage.logout();
//await new Promise((resolve) => setTimeout(resolve, 3000));
await page.close();
await context.close();
await browser.close();
}
export default globalTeardown;
test-cases.config.ts :
{
"testCases": [
{
"file": "./src/app/tests/designers/module-designer/create-module.spec.ts",
"tags": ["create-new-project"]
},
{
"file": "./src/app/tests/designers/entity-designer/create-entity.spec.ts",
"tags": ["test-case-1"]
}
]
}
test-runner.ts :
import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';
const execAsync = promisify(exec);
async function runTests() {
const config = JSON.parse(fs.readFileSync('./test-cases.config.json', 'utf-8'));
const testCases = config.testCases;
for (const { file, tags } of testCases) {
for (const tag of tags) {
try {
console.log(Running tests in ${file} with tag @${tag}
);
await execAsync(npx playwright test ${file} --grep "@${tag}"
);
} catch (error) {
console.error(Error running tests in ${file} with tag @${tag}:
, error);
process.exit(1); // Exit on failure
}
}
}
console.log('All tests passed');
}
runTests().catch(err => {
console.error(err);
process.exit(1);
});
playwright.config.ts :
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
globalSetup: require.resolve('./src/app/global/global-setup'),
globalTeardown: require.resolve('./src/app/global/global-teardown'),
use: {
headless: false,
channel: 'chrome',
screenshot: 'on',
video: 'on',
viewport: { width: 1920, height: 970 },
launchOptions: {
slowMo: 3500,
args: ['--window-position=-5,-5'],
},
trace: 'on',
},
timeout: 1200000,
testDir: 'src/app/tests',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : 1,
reporter: 'html',
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], storageState: './localStorageState.json' },
},
// Define other projects as needed...
],
});
1st spec file :
import { test, expect, Page } from '@playwright/test';
import moduleDesignerTestConfig from "../../../../app/data/module-designer-test.config.json";
import { MenuNavigatorPage } from '../../../pages/menu-navigator.page';
import { ModuleDesignerPage } from '../../../pages/designers/module-designer/module-designer.page';
import * as fs from 'fs';
import { loadStorageState } from '../../../../../utils/utils';
import { LoginPage } from '../../../pages/login.page';
test.describe.configure({ mode: 'serial' });
let menuNavigatorPage: MenuNavigatorPage;
let moduleDesignerPage: ModuleDesignerPage;
test.beforeAll(async ({ browser }) => {
const context = await browser.newContext({ storageState: 'localStorageState.json' });
const page = await context.newPage();
const origin = await loadStorageState(context);
await page.goto(origin);
menuNavigatorPage = new MenuNavigatorPage(page, context);
moduleDesignerPage = new ModuleDesignerPage(page, context);
});
test('Create New Module by creating new project @create-new-project', async () => {
const projectName = await moduleDesignerPage.projectDesignerPage.createProject();
if (projectName) {
const moduleName = await moduleDesignerPage.createNewModule(projectName);
expect(moduleName).toContain(moduleDesignerTestConfig.module.moduleName);
} else {
throw new Error('Failed to create project');
}
});
test('Create New Module in existing project', async () => {
const projectName = moduleDesignerTestConfig.module.projectNameForModule;
if (projectName) {
const moduleName = await moduleDesignerPage.createNewModule(projectName);
expect(moduleName).not.toBeNull();
} else {
throw new Error('Project name for module is not defined in the config');
}
});
----- 2nd spec file
import { test, expect, BrowserContext, Page } from '@playwright/test';
import { LoginPage } from '../../../pages/login.page';
import testConfig from "../../../../../test.config.json";
import { MenuNavigatorPage } from '../../../pages/menu-navigator.page';
import { getRandomNumber, loadStorageState } from '../../../../../utils/utils';
import { EntityDesignerPage } from '../../../pages/designers/entity-designer/entity-designer.page';
import { TestSuite } from '../../../common/test-suite.enum';
import entityDesignerTestConfig from "../../../data/entity-designer-test.config.json";
import { IEntity } from '../../../pages/designers/entity-designer/entity-designer.model';
import * as fs from 'fs';
test.describe(TestSuite.entityDesigner, () => {
test.describe.configure({ mode: 'serial' });
let menuNavigatorPage: MenuNavigatorPage;
let entityDesignerPage: EntityDesignerPage;
let context: BrowserContext;
let page: Page;
test.beforeAll(async ({ browser }) => {
const context = await browser.newContext({ storageState: 'localStorageState.json' });
const page = await context.newPage();
const origin = await loadStorageState(context);
await page.goto(origin);
menuNavigatorPage = new MenuNavigatorPage(page, context);
entityDesignerPage = new EntityDesignerPage(page, context)
});
// test.afterEach(async ({ page, context }) => {
// await loginPage.logout();
// });
test('Create New Entity with main table by creating new project @test-case-1', async () => {
let projectName = await entityDesignerPage.projectDesignerPage?.createProject();
let entityName = await entityDesignerPage.createNewEntity(projectName as string
, testConfig.entityTest.entityName + getRandomNumber());
expect(entityName).toContain(testConfig.entityTest.entityName);
});
test('Create New Entity with main table using existing project', async () => {
let entityName = await entityDesignerPage.createNewEntity(testConfig.entityTest.projectNameForEntity,
testConfig.entityTest.entityName + getRandomNumber());
expect(entityName).toContain(testConfig.entityTest.entityName);
});
test('Create Standard Entity In Standard Automation Project', async () => {
let entityName = await entityDesignerPage.createNewEntity(testConfig.standard.projectName,
testConfig.standard.entity.entityName);
expect(entityName).toBe(testConfig.standard.entity.entityName);
});
test('Create New Entity with main table for Standard project', async () => {
const entity: IEntity = entityDesignerTestConfig.entity;
let entityName = await entityDesignerPage.createNewEntityForStandardProject(testConfig.standard.projectName, entity);
expect(entityName).toContain(testConfig.standard.entity.entityName);
});
});
---- this is common file for function:
import { ControlEvent, HttpVerb } from "../src/app/common/common.model";
import { Response } from "playwright-core";
import { BrowserContext, Page } from '@playwright/test';
import * as fs from 'fs';
export async function saveStorageState(context: BrowserContext, page: Page) {
// Save localStorage state
await context.storageState({ path: 'localStorageState.json' });
// Get session storage and store as a file
const sessionStorage: any = await page.evaluate(() => JSON.stringify(sessionStorage));
fs.writeFileSync('sessionStorage.json', sessionStorage, 'utf-8');
}
export async function loadStorageState(context: BrowserContext) {
// Load the saved storage state
const savedStorageStateStr = fs.readFileSync('localStorageState.json', 'utf8');
const savedStorageState = JSON.parse(savedStorageStateStr);
// Manually set cookies
await context.addCookies(savedStorageState.cookies);
const origin = savedStorageState.origins[0].origin;
// Manually set local storage
const sessionStorage = JSON.parse(fs.readFileSync('sessionStorage.json', 'utf-8'));
await context.addInitScript(storage => {
for (const [key, value] of Object.entries(storage)) {
window.sessionStorage.setItem(key, value as string);
}
}, sessionStorage);
}
Top comments (0)