Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Not clear how to create wrapper and fail test with browser.mock #910

Closed
1 task
olegKusov opened this issue Apr 18, 2024 · 2 comments
Closed
1 task

Not clear how to create wrapper and fail test with browser.mock #910

olegKusov opened this issue Apr 18, 2024 · 2 comments
Assignees

Comments

@olegKusov
Copy link

olegKusov commented Apr 18, 2024

Verify latest release

  • I verified that the issue exists in the latest Hermione release

Hermione version

8.0.4

Last Hermione version that worked

No response

Which area(s) of Hermione are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue or a replay of the bug

No response

Reproduction steps

I want to create custom command for browser.mock

module.exports = async function (url, cb, opts) {
    return new Promise(async (resolve, reject) => {
        const mock = await this.mock(url, {fetchResponse: false});
        mock.respond((request) => {
            try {
                 resovle();
                return cb(request);
            } catch(e) {
                reject(e);
            }
        }, {
            fetchResponse: false,
            ...opts,
        })
    });
};

The problem is that its not clear how to make it work.

Actual Behavior

browser.addCommand('loadPageWithout404', (url, {selector, predicate}) => new Promise(async (resolve, reject) => {
    const mock = await this.mock('**')

    mock.on('match', ({url, statusCode}) => {
        if (statusCode === 404) {
            reject(new Error(`request to ${url} failed with "Not Found"`))
        }
    })

    await this.url(url).catch(reject)

    // waiting here, because some requests can still be pending
    if (selector) {
        await this.$(selector).waitForExist().catch(reject)
    }

    if (predicate) {
        await this.waitUntil(predicate).catch(reject)
    }

    resolve()
}))

In webdriverio docs there is one example. And as far as I understand they waiting for all requests are done and only after that they can be sure that mock.on will be called. But its very strange logic. What if i click to button in my test after page load and request will be 404. then this command will no work (e.g. it will not fail test). And with this logic we need to create mock every time we making some actions and not at the top level of test how I understand it, e.g

await this.browser.loadWithout404('');

await this.browser.loadWithout404('/button_action_url', {selector: 'some_selector_after_action'});
await this.browser.$(...).click();

One more thing that looks strange to me is that statusCode handled separately. So if we want to mock request with some data and change status Code then we need to duplicate logic

        await this.browser.myMockCommand('**' + '/api/...', (req) => {
            if(req.postData !== JSON.stringify(mock)) {
                      return {res: 'body is wrong'};
            }
        }, {statusCode: (data) =>  req.postData !== JSON.stringify(mock) ? 404 : 200}
           })

Expected Behavior

I just want to get how to fail test if mock.respond returns error.

Which Node.js version are you using?

18.12.1

@olegKusov olegKusov changed the title Not clear how to create wrapper for browser.mock Not clear how to create wrapper and fail test with browser.mock Apr 18, 2024
@shadowusr shadowusr assigned shadowusr and unassigned shadowusr Apr 25, 2024
@KuznetsovRoman KuznetsovRoman self-assigned this May 2, 2024
@KuznetsovRoman
Copy link
Member

KuznetsovRoman commented May 2, 2024

If you want to create a command, then yes, browser command can only resolve and reject, and resolved command can't reject afterwards.

With webdriverio mock callbacks, i can offer you this approach:

  • add testplane-global-hook plugin
  • add network spy on beforeEach to listen for network responses and write meta info to the browser if 404 appeared
  • check for 404 responses on afterEach and remove network spy

In code, it would look like this:

// .testplane.conf.ts
browser.mockRestoreAll()

module.exports = {
    plugins: {
        '@testplane/global-hook': {
            beforeEach: async ({browser}) => {
                const networkSpy = await browser.mock("**"); // create network spy

                networkSpy.on('match', async ({statusCode, url}) => {
                    if (statusCode === 404) {
                        await browser.setMeta("notFoundUrl", url); // set meta property to check if in afterEach
                        // Note: we can't reject or throw errors inside "on" callback, because it would produce unhandled rejection
                    }
                });
            },
            afterEach: async ({browser}) => {
                await browser.mockRestoreAll(); // unsubscribe to browser mocks

                const notFoundUrl = await browser.getMeta("notFoundUrl");

                if (notFoundUrl) {
                    // but we can throw an error here
                    throw new Error("Not found at " + url);
                }
            }
        },
        // other testplane plugins
    },

    // other testplane settings...
};

As i mentioned, we can't throw errors inside of browserMock.on callback, as well as we can't just interrupt test body execution from somewhere outside of it.

If you want to, you can overwrite "url" command so it would check for browser.getMeta("notFoundUrl") after "url" resolve, but for "click" part it is much harder. All we can see is:

  • browser.click was called
  • browser.click resolved (clicked)
  • some amount of milliseconds passed
  • some network request failed.

We can't tell if network request was called because of click. Click itself could be the reason and could not be the reason of that network request. Even wrapping "url" will not 100% help you because it only waits for document.readyState to be complete, but you might have some lazy network requests.

With the url part, i can offer you openAndWait command: https://github.com/gemini-testing/testplane/blob/master/docs/writing-tests.md#openandwait

It would wait for page to load and check for network errors. Notice how you still need to specify waitNetworkIdleTimeout, because there could be a gap between one request resolved and the second one started (for example, when your react bundle just loaded, it needs to be processed, and only then your fetch in useEffect is called)

@DudaGod
Copy link
Member

DudaGod commented May 13, 2024

I am closing it due to inactivity.

@DudaGod DudaGod closed this as completed May 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants