Issue facing with Global Setup
Published: 2024-07-02 16:40:51

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lkjcwd1vhsjevcw47k9t.JPG)
return origin;

}