checkly / headless-recorder

Chrome extension that records your browser interactions and generates a Playwright or Puppeteer script.
https://checklyhq.com/headless-recorder
MIT License
15.03k stars 722 forks source link

Handle Assertions #87

Closed Fmstrat closed 1 year ago

Fmstrat commented 4 years ago

Hi,

I think a nice feature of PR would be to include the ability to handle assertions. I.E:

"Confirm that a piece of content exists on a page after an action is taken."

I do this manually in Puppeteer with a variation of the below block of code (specifically the page.assert function).

page = await browser.newPage()

page.announce = function(title) {
    console.log(`Running test: ${title}`);
}

page.success = function(test) {
    console.log(`  Success: ${test}`);
}

page.fail = function(test) {
    page.pass = false;
    console.log(`  -=- FAIL -=-: ${test}`);
}

page.assert = async function(test, location, value) {
    res = await this.getPage();
    switch (location) {
        case "body":
            if (res.body.includes(value)) {
                this.success(test)
            } else {
                this.fail(test)
            }
            break;
        case "title":
            if (res.title.includes(value)) {
                this.success(test)
            } else {
                this.fail(test)
            }
            break;
    }
},

page.getPage = async function() {
    await this.sleep(500);
    return {
        title: await this.title(),
        body: await this.evaluate(() => document.body.innerHTML)
    }
},

page.sleep = async function(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

page.pass = true;

// Announce the test is starting
page.announce("Sample Test");

// Set the view to a desktop/laptop window
await page.setViewport({ width: 1920, height: 980 })

// The navigationPromise means: "wait for the page to finish loading after a click"
const navigationPromise = page.waitForNavigation()

// Load a page and make sure "Moodle sandbox demo" is in the page title
await page.goto(env.url)
await page.assert("Initial load", "title", "Sandbox demo");

// Click on the login link in the upper right
await page.waitForSelector('.nav > .nav-item > .usermenu > .login > a')
await page.click('.nav > .nav-item > .usermenu > .login > a')
await navigationPromise

// Click on the username field
await page.waitForSelector('#username')
await page.click('#username')

// Type in username from our environments file
await page.type('#username', env.user)

// Type in password (tab got us here without clicking)
await page.type('#password', env.pass)

// Click the login button and make sure "Admin User" is in the page body afterwards
await page.waitForSelector('#loginbtn')
await page.click('#loginbtn')
await navigationPromise
await page.assert("Logged in", "body", "Admin User");

// Click on "My first course" and make sure "Topic 1" is in the page body afterwards
await page.waitForSelector('.courses > .even > .info > .coursename > a')
await page.click('.courses > .even > .info > .coursename > a')
await navigationPromise
await page.assert("First course", "body", "Topic 1");

// Click the dropdown menu in the upper right
await page.waitForSelector('#action-menu-toggle-1')
await page.click('#action-menu-toggle-1')

// Click on logout and make sure "You are not logged in." is in the body of the page
await page.waitForSelector('#action-menu-1-menu #actionmenuaction-6')
await page.click('#action-menu-1-menu #actionmenuaction-6')
await navigationPromise
await page.assert("Logout", "body", "You are not logged in.");
...

I think an amazing feature (that can be reused in your path forward even if it's not puppeteer as most apps, even playwrite, support this, would be to add a right click menu to include an assertion.

The process could work like this:

The code would then generate a shell with similar added page functions to the above, along with:

await page.waitForSelector('#selector')
await page.assert("Found: Text in selector", "#selector", "Text in selector");

Where for assert the first argument is what is being searched prefixed with "Found:", the second is the CSS selector of where that object is on the page, and the third is the actual text to search for.

Much of the assert function from above can be leveraged, you already have the code to grab the CSS selector, it just requires the right-click functionality to be added.

Thoughts?

Fmstrat commented 4 years ago

As a note, this is what it looks like when I run the above test (in the framework I've created) which then provides return codes based on if success came back (page.pass).

01

tnolet commented 4 years ago

@Fmstrat thanks for contributing, and this is absolutely something we want to add. First response after reading your proposal:

  1. For assertions, we would 99% sure use an existing assertion library, i.e. chai.js, Node's own assert and/or the Jest expect function.

  2. A right-click, "add assertion" workflow is exactly what I was thinking. Maybe even a custom in page menu to provide some more options.

ianaya89 commented 1 year ago

https://github.com/checkly/headless-recorder/issues/232