Michael Lynch (@deliberatecoder)
| Task | Cypress | Playwright | Difference | 
|---|---|---|---|
| Run tests on CircleCI | 127s | 84s | -34% | 
| Run tests from development machine | 40s | 7s | -83% | 
should, expect, and assert depending on contextThese two snippets are functionally equivalent
cy.get("#error-message").should("be.visible");
cy.get("#error-message").should(($el) => expect($el).to.be.visible);
expect(page.locator("#error-message")).toBeVisible();
// Save the route to the guest link URL so that we can return to it later.
cy.get('.table td[test-data-id="guest-link-label"] a')
  .invoke("attr", "href")
  .then(($href) => {
    // Log out.
    cy.get("#navbar-log-out").click();
    cy.location("pathname").should("eq", "/");
    // Make sure we can still access the guest link after logging out.
    cy.visit($href);
    // Continue with the test
  });
then() doesn’t return a real Promise objectthen() support await
Promises// Save the route to the guest link URL so that we can return to it later.
const guestLinkRouteValue = await page
  .locator('.table td[test-data-id="guest-link-label"] a')
  .getAttribute("href");
expect(guestLinkRouteValue).not.toBeNull();
const guestLinkRoute = String(guestLinkRouteValue);
// Log out.
await page.locator("#navbar-log-out").click();
await expect(page).toHaveURL("/");
// Make sure we can still access the guest link after logging out.
await page.goto(guestLinkRoute);
// Continue with the test.
<p data-test-id="github-instructions">
  Visit our
  <a href="https://github.com/mtlynch/picoshare">Github repo</a> to create your
  own PicoShare server.
</p>
Naive test:
cy.get("[data-test-id='github-instructions']").should(
  "have.text",
  "Visit our Github repo to create your own PicoShare server."
);
Timed out retrying after 10000ms
+ expected - actual
-'\n      Visit our\n      Github repo to create\n      your own PicoShare server.\n    '
+'Visit our Github repo to create your own PicoShare server.'
<p data-test-id="github-instructions">
  Visit our
  <a href="https://github.com/mtlynch/picoshare">Github repo</a> to create your
  own PicoShare server.
</p>
Correct test:
cy.get("[data-test-id='github-instructions']").should(($el) => {
  expect($el.get(0).innerText).to.eq(
    "Visit our Github repo to create your own PicoShare server."
  );
});
<p data-test-id="github-instructions">
  Visit our
  <a href="https://github.com/mtlynch/picoshare">Github repo</a> to create your
  own PicoShare server.
</p>
await expect(page.locator("data-test-id=github-instructions")).toHaveText(
  "Visit our Github repo to create your own PicoShare server."
);
webServer: {
  command: "PS_SHARED_SECRET=dummypass PORT=6001 ./bin/picoshare",
  port: 6001,
},
console.log("hello from Cypress"); // this does nothing
cy.log("hello from Cypress"); // this prints nothing to the terminal
console.log in Playwright just worksconsole.log("hello from Playwright");
[chromium] › auth.spec.ts:3:1 › logs in and logs out
hello from Playwright
cy.get(".navbar-item [data-test-id='log-in']").should("be.visible");
How Playwright works:
await expect(
  page.locator(".navbar-item [data-test-id='log-in']")
).toBeVisible();
What I want:
// INVALID - not how Playwright actually behaves
await page
  .locator(".navbar-item [data-test-id='log-in']")
  .expect()
  .toBeVisible();
Promises