The playwright signs in to the application through the cached session in global-setup.ts, where the wait for the selector times out when DEBUG=0 but does not time out when DEBUG=

*Updates 22.6.2022 / Identified a recurring issue with OAuth on another site, where globalSetup fails to function properly on the OAuth domain. 21.6.2022 / Analysis from Trace.zip reveals that the OAuth login URL is correct, but the screenshot depicts a blank white page.

My Objective:

I aim to implement cached authentication in my Playwright TypeScript test project. Following the guidelines provided here - .

This approach involves performing the login process once in the global-setup.ts file before each test, and then caching it for subsequent tests.

The Challenge:

The test fails with a timeout error of 30000ms during the page.click action: When using $env:PWDEBUG=1, there are no issues. However, setting it to 0 triggers the problem.

Summary of Workflow:

  1. Main login page located at domain abc.com

  2. Click on "login by email"

  3. Redirects to a different domain featuring the email login form (where < [placeholder="Email Address"] exists).

Observations: After capturing screenshots and logs, it appears that with DEBUG=0, the desired element is not found on the respective page. In workflow step #1, upon clicking the "login with email" button, the subsequent email login form page does not become visible due to unknown reasons. I attempted incorporating additional timeouts, increased load waiting times, etc., without success. Note: If the post-click login page redirects to a different domain, it might be relevant, although this isn't an issue when DEBUG=1.

Everything functions correctly when solely utilizing beforeEach; however, this contradicts the instructions outlined in the Playwright documentation.

npx playwright test

Running 3 tests using 1 worker

page.click: Timeout 30000ms exceeded.
=========================== logs ===========================
waiting for selector "[placeholder="Email Address"]"
============================================================

   at ..\global-setup.ts:19

  18 |   // Click [placeholder="Email Address"]
> 19 |   await page.click('[placeholder="Email Address"]');
import { chromium, FullConfig } from '@playwright/test';

async function globalSetup(config: FullConfig) {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('https://website.myapp.fi/app/');

  // Attempted the operation without waitForNavigation with no variance in output
  await Promise.all([
    page.waitForNavigation(),
    page.locator('div[role="button"]:has-text("email")').click(),
  ]);

  // Click [placeholder="Email Address"]
  await page.click('[placeholder="Email Address"]');
  await page.locator('[placeholder="Email Address"]').fill('email..');

  // Click [placeholder="Password"]
  await page.click('[placeholder="Password"]');
  await page.locator('[placeholder="Password"]').fill('password..');

  // Click button:has-text("Sign in")
  await page.click('button:has-text("Sign in")');

  // Select company
  await page.click('.b-number-cell');
  await page.waitForLoadState('networkidle');
  // Save signed-in state to 'storageState.json'.
  await page.context().storageState({ path: 'storageState.json' });
  await browser.close();
}

export default globalSetup;

EDIT: added playwright.config.ts

import type { PlaywrightTestConfig } from '@playwright/test';
import { devices } from '@playwright/test';

const config: PlaywrightTestConfig = {
  globalSetup: require.resolve('./global-setup'),
  testDir: './tests',
  timeout: 30 * 1000,
  expect: {
    timeout: 5000
  },
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 2 : 0,
  workers: process.env.CI ? 1 : undefined,
  reporter: 'html',
  use: {
    actionTimeout: 0,
    trace: 'on-first-retry',
    // Inform all tests to load signed-in state from 'storageState.json'.
    storageState: 'storageState.json'
  },
  projects: [
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
      },
    },
  ],
};

export default config;

Answer №1

Based on the information provided, it seems like the timeout specified is 30 seconds. It is clear that in debug mode, the test is not required to adhere to this timeframe. I suggest you consider increasing this as a preliminary step.

timeout: 60 * 1000,

Furthermore, since the error pertains to a specific action -

waiting for selector "[placeholder="Email Address"]"

You should also establish a timeout for that particular action -

await page.click('[placeholder="Email Address"]', { timeout: 60000 });

Answer №2

I successfully resolved the issue by implementing a separate login test to kick off the test suite, rather than relying on globalSetup.

The login process involves saving cookies, localStorage, and sessionStorage:

test.describe('Login suite', () => {
    test("confirming successful login", async ({ page, context }) => {

        // ...
        // The login steps are executed here first, followed by caching necessary values once completed
        
        // Save cookies and localstorage to a file for future use in automatically logging in
        await context.storageState({ path: 'state.json' });

        // Retrieve session storage and store as environment variable
        const sessionStorage = await page.evaluate(() => JSON.stringify(sessionStorage));
        process.env.SESSION_STORAGE = sessionStorage;

        // Log sessionStorage and localStorage after storing them for debugging purposes
        const localStorage = await page.evaluate(() =>
            JSON.stringify(window.localStorage)
        );
    });
});

Subsequently, each test utilizes cookies and localStorage with "test.use({ storageState: 'state.json' });" while sessionStorage is populated from an environmental variable. Due to context.storageState not saving sessionStorage, an alternative method must be utilized.

test.describe('Test suite', () => {
    test.use({ storageState: 'state.json' });
    test("testing functionality", async ({ page, context }) => {
    
        // Load session storage into a new context
        const sessionStorage = process.env.SESSION_STORAGE;

        await context.addInitScript(storage => {
            console.log(window.location.hostname);
            if (window.location.hostname === 'mysite.eu') {
                const entries = JSON.parse(storage);
                for (const [key, value] of Object.entries(entries)) {
                    window.sessionStorage.setItem(key, value);
                }
            }
        }, sessionStorage);
        
        // ...
        // The actual testing procedures take place here
    });
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What causes the oninput event to act differently in Angular as opposed to regular JavaScript?

As I delve into learning Angular with TypeScript, I've encountered some inconsistencies compared to JavaScript that are puzzling me. Take for example this function that works flawlessly with pure JavaScript, where it dynamically sets the min and max a ...

Issue with Destructuring Assignment Syntax in TypeScript

interface User extends Function { player: number, units: number[], sites: string[], } class User extends Function { constructor() { super('return this.player') [this.player, this.units, this.sites] = getBelongings( ...

Challenge your TypeScript skills: convert snake_case to camelCase and back again

I am looking to develop a Typescript function that can deeply traverse a plain object and convert all snake_case keys to camelCase. Additionally, I need a function that can convert camelCase keys to snake_case throughout the object. While implementing thi ...

What is the best way to utilize the existing MUI state in order to calculate and show column totals?

I am currently in the process of developing an MUI web application to keep track of some personal data. Within this application, I have incorporated the MUI datagrid pro component to efficiently display the data with its robust filtering capabilities. In ...

How can you create a scenario in Angular Material where items in a toolbar transition to the second line exclusively on mobile screens?

Visit the Angular Material website to see how the toolbar appears on a desktop: https://material.angular.io/ https://i.sstatic.net/KPFMv.png On mobile devices, the menu items Components, CDK, and Guides are displayed on the second line, while github, the ...

Boost the Gen 2 User Directory

After reviewing all of the Amplify Gen 2 Documentation, I am struggling to find a way to display a list of registered users within my application. I need to create an admin page in Angular that will showcase all users along with their respective roles. I ...

Finding and retrieving specific information from nested arrays within a JSON object can be done by implementing a method that filters the data

I have an array of objects where each object contains a list of users. I need to be able to search for specific users without altering the existing structure. Check out the code on StackBlitz I've tried implementing this functionality, but it's ...

What is the best way to handle missing values in a web application using React and TypeScript?

When setting a value in a login form on the web and no value is present yet, should I use null, undefined, or ""? What is the best approach? In Swift, it's much simpler as there is only the option of nil for a missing value. How do I handle ...

Tips for choosing and filtering the preferred object in ES6

Consider this array structure: const testData = [ { group: "Team1", info: [ { key: 123, person: "Alice", type: "Football" }, { key: 456, person: "Bob", type: " ...

Is my approach to CSV parsing correct if I am receiving the error "Unable to assign property 'processing' to undefined"?

In our database, we store words along with their categories. I have a new requirement to enable the word administrator to upload multiple words at once using a CSV file. Currently, our system only allows for single-word additions at a time. My solution was ...

Unable to locate the name 'Cheerio' in the @types/enzyme/index.d.t file

When I try to run my Node application, I encounter the following error: C:/Me/MyApp/node_modules/@types/enzyme/index.d.ts (351,15): Cannot find name 'Cheerio'. I found a suggestion in a forum that recommends using cheerio instead of Cheerio. H ...

What could be causing the malfunction of the constructor of these two root services?

Below are two primary root services: Service 1: @Injectable({ providedIn: 'root', }) export class DataService { private data$ = new BehaviorSubject([]); public dataObs$ = this.data$.asObservable(); constructor(private http: HttpClient) ...

Using Angular NgUpgrade to inject an AngularJS service into an Angular service results in an error message stating: Unhandled Promise rejection: Cannot read property 'get' of undefined; Zone:

I have noticed several similar issues on this platform, but none of the solutions seem to work for me. My understanding is that because our Ng2App is bootstrapped first, it does not have a reference to $injector yet. Consequently, when I attempt to use it ...

Leveraging Express for delegating requests to an ADFS server

We are currently facing a challenge with authenticating to our on-premise ADFS 3.0 server. Our Angular application, a single-page app, needs to authenticate with the ADFS server. However, we are encountering CORS issues due to them not being on the same se ...

Importing node_modules in Angular2 using TypeScript

My Angular2 app started off as a simple 'hello world' project. However, I made the questionable decision to use different directory structures for my development environment and the final deployment folder on my spring backend. This difference c ...

Tips for targeting a specific element with providers in Ionic

By using the specified pattern, I am aiming to achieve a unique toolbar or header for only certain pages. Is there a way to accomplish this without injecting the provider and keeping the page as a standalone? My understanding of Ionic is still developing, ...

What sets React.FC<T> apart from Function() in TypeScript?

Do arrow functions and function notations differ from each other? It seems like these two methods function in the same way. One is written as React.FC<T> while the other as Function(). Is this simply a matter of notation, or are there deeper reaso ...

What could be causing the Ioncol not triggering the Onclick event?

I am facing an issue where my onclick event is not working on an ion-col. The error message says that the method I call "is not defined at html element.onclick". Here is a snippet of my code: <ion-row style="width:100%; height:6%; border: 1px solid # ...

Displaying messages in an Angular 2 chatbox from the bottom to the top

As a newcomer to using typescript, I am currently working on an angular 2 project and facing some challenges with creating a chatbox. My goal is to have new messages displayed at the bottom while pushing the old ones up one line at a time, as shown in this ...

Utilizing Vue 3's Inject Plugin alongside the powerful Composition API and TypeScript

I developed a controller plugin to be used globally in all components, but I am facing challenges making it compatible with Vue 3 + TypeScript + Composition API as I keep getting a TypeScript error. ui/plugins/controllers.ts import { App } from 'vue& ...