Search code examples
node.jstypescriptplaywright

Why am I seeing a weird output on Expected Value from Playwright TypeScript on my Windows PC?


So, I'm using a Windows PC (It matters; I think) and I'm trying to access a website's elements using Playwright and TypeScript. I am trying to get the heading of a element in the DOM, so I'm using the following code (using http://automationintesting.online as my baseline):

StepFunction.ts:

import { expect } from "@playwright/test";
import "core-js";
import { Before, Given, Then, After, ITestCaseHookParameter, Status } from "@cucumber/cucumber";
import { ChromiumBrowser, chromium } from "@playwright/test";
import { AllPagesObject } from "../../pages/AllPagesObject.js";
import { ensureDir } from "fs-extra";

import ICustomWorld from "../../support/custom-world.js";
import getEnvConfig from "../../config/index.js";

const tracesDir = "traces";

declare global {
  namespace NodeJS {
    interface Global {
      browser: ChromiumBrowser;
    }
  }
  interface Window {
    dataLayer: any;
    localStorage: any;
  }
}

Before(
  async function (this: ICustomWorld, { pickle }: ITestCaseHookParameter) {
    const envConfig = getEnvConfig();
    let homePageURL = envConfig.automationInTesting.HOMEPAGE_URL;

    let browser = await chromium.launch({
      headless: false,
      args: [
        "--start-maximized",
        "--disable-blink-features=AutomationControlled"
      ],
    });
    let context = await browser.newContext({viewport: { width: 1920, height: 1024 },});
    let page = await context.newPage();
    await ensureDir(tracesDir);
    this.startTime = new Date();
    this.browser = browser;
    this.context = context;
    this.context.setDefaultTimeout(30000);
    await this.context.tracing.start({ screenshots: true, snapshots: true });
    this.page = page;
    this.automationInTestingPageObject = new AllPagesObject(this.page, this.context);
    this.startTime = new Date();
    this.testName = pickle.name.replace(/\W/g, "-");
    this.homePageURL = homePageURL;
    this.feature = pickle;
  }
);

Given("I am a new user to the Automation In Testing website", { timeout: 240000 }, async function (this: ICustomWorld) {
   await this.automationInTestingPageObject.homePage.goTo(this.homePageURL);
   await this.automationInTestingPageObject.homePage.pageLoaded();
});

Then("I have landed on the home page", { timeout: 240000 }, async function (this: ICustomWorld) {
  expect(this.automationInTestingPageObject.homePage.elements.welcomeHeading).toBe("Welcome to Restful Booker Platform");
});

After({timeout: 240 * 1000},
  async function (this: ICustomWorld, { result }: ITestCaseHookParameter) {
    if (result) {
      this.attach(
        `Status: ${result?.status}. Duration:${result.duration?.seconds}s`
      );

      const formattedTime = this.startTime
        ?.toISOString()
        .split(".")[0]
        .replace(/:/g, "_");

      await this.context?.tracing.stop({
        path: `${tracesDir}/${this.testName}-${formattedTime}trace.zip`,
      });

      if (result.status !== Status.PASSED) {
        await this.automationInTestingPageObject.basePage.screenshot(`${this.feature?.name}`);
      }
    }

    await this.automationInTestingPageObject.page.close();
    await this.context.close();
    await this.browser.close();
  }
);

Custom-World.ts:

import { AllPagesObject } from "../pages/AllPagesObject.js";
import { setWorldConstructor, World, IWorldOptions } from "@cucumber/cucumber";
import * as messages from "@cucumber/messages";
import { BrowserContext, Page, ChromiumBrowser } from "@playwright/test";

export interface CucumberWorldConstructorParams {
  parameters: { [key: string]: string };
}

export interface CustomWorldBeforeSetup extends World {
  feature?: messages.Pickle;
  context?: BrowserContext;
  page?: Page;
  homePageURL?: string;
  testName?: string;
  startTime?: Date;
  automationInTestingPageObject?: AllPagesObject;
  browser?: ChromiumBrowser;
}

export default interface ICustomWorld extends CustomWorldBeforeSetup {
  feature: messages.Pickle;
  context: BrowserContext;
  page: Page;
  homePageURL: string;
  testName: string;
  startTime: Date;
  automationInTestingPageObject: AllPagesObject;
  browser: ChromiumBrowser;
}

export class CustomWorld extends World implements CustomWorldBeforeSetup {
  constructor(options: IWorldOptions) {
    super(options);
  }
}

setWorldConstructor(CustomWorld);

AllPagesObject.ts:

import { BasePage } from "./BasePage.js";
import { HomePage } from "./HomePage.js";
import { Page, BrowserContext } from "@playwright/test";

export class AllPagesObject {
  basePage: BasePage;
  homePage: HomePage;

  constructor(public page: Page, public context: BrowserContext) {
    this.basePage = new BasePage(page, context);
    this.homePage = new HomePage(page, context);
  }
}

HomePage.ts:

import { BasePage } from "./BasePage.js";

export class HomePage extends BasePage {
    public get elements() {
        return {
           welcomeHeading: this.page.getByRole("heading")
        };
    }
}

I don't think the rest is fully relevant code, but essentially, when I run this on my windows machine; I get the following error:

PS C:\Users\[ME]\Documents\projects\[THIS-PROJ]> npm run dev
> [email protected] dev
> NODE_OPTIONS="--loader="ts-node/esm"" ENV=dev PWVIDEO=false npx cucumber-js --tags "@Test"

(node:5952) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:5952) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:4260) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:4260) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
(node:19532) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:19532) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
`publishQuiet` option is no longer needed, you can remove it from your configuration; see https://github.com/cucumber/cucumber-js/blob/main/docs/deprecations.md
Feature: Navigate around the Automation In Testing Online website # features\AutomationInTesting.feature:1

  @Test
  Scenario: Load the Automation In Testing Website # features\AutomationInTesting.feature:4
    Given I am a new user to the Automation In Testing website
    Then I have landed on the home page
    × failed
      Error: expect(received).toBe(expected) // Object.is equality

      Expected: "Welcome to Restful Booker Platform"
      Received: {"_frame": {"_guid": "frame@5592e7d0f624ae84eb2d96a490ed9727", "_type": "Frame"}, "_selector": "#frame >> internal:role=heading"}
          at Proxy.<anonymous> (C:\Users\[ME]\Documents\projects\[THIS-PROJ]\node_modules\playwright\lib\matchers\expect.js:194:37)
          at World.<anonymous> (file:///C:/Users/[ME]/Documents/projects/[THIS-PROJ]/features/step_definitions/HomePageSteps.ts:20:85)
          at file:///C:/Users/[ME]/Documents/projects/[THIS-PROJ]/features/step_definitions/HomePageSteps.ts:7:71
          at __awaiter (file:///C:/Users/[ME]/Documents/projects/[THIS-PROJ]/features/step_definitions/HomePageSteps.ts:3:12)
          at World.<anonymous> (file:///C:/Users/[ME]/Documents/projects/[THIS-PROJ]/features/step_definitions/HomePageSteps.ts:19:12)

I'm not sure why I'm seeing an error that effectively says there's a frame in the way when I know there's not in the DOM (And you can check the DOM yourselves at the URL I provided)

Can anyone think of what I missed here? Why is it complaining about a frame of some description?


Solution

  • Looks like you are not using the right assertion.

    expect(this.automationInTestingPageObject.homePage.elements.welcomeHeading).toBe("Welcome to Restful Booker Platform");
    

    You are checking whether the welcomeHeading object (a Playwright Locator) is equal to the string "Welcome to Restful Booker Platform" by using the toBe assertion, which checks for object equality.

    Clearly those are not the same object. That's why you are getting this error message, which tells you the same:

    Error: expect(received).toBe(expected) // Object.is equality
    
    Expected: "Welcome to Restful Booker Platform"
    Received: {"_frame": {"_guid": "frame@5592e7d0f624ae84eb2d96a490ed9727", "_type": "Frame"}, "_selector": "#frame >> internal:role=heading"}
    

    If you want to ensure that the heading element contains some expected text, you should use the toHaveText assertion instead:

    await expect(this.automationInTestingPageObject.homePage.elements.welcomeHeading).toHaveText("Welcome to Restful Booker Platform");
    

    Maybe have a look at Playwright's assertion guide.