DEV Community

chandansh27
chandansh27

Posted on

Issue facing with Global Setup

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();
Enter fullscreen mode Exit fullscreen mode

}


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();
Enter fullscreen mode Exit fullscreen mode

// 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();
Enter fullscreen mode Exit fullscreen mode

}

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"]
}

]
Enter fullscreen mode Exit fullscreen mode

}

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);
Enter fullscreen mode Exit fullscreen mode

});

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');
}
Enter fullscreen mode Exit fullscreen mode

});


----- 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;
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

});

---- 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);

Image description
return origin;

}

Top comments (0)