webdriverio-community / wdio-intercept-service

🕸 Capture and assert HTTP ajax calls in webdriver.io
MIT License
105 stars 33 forks source link

Proposal : fix for intercepting HTTP calls during page load #185

Open Badisi opened 2 years ago

Badisi commented 2 years ago

While trying to intercept http requests originating from an Angular application, I came accross the following restriction mentionned in the README:

There's one catch though: you can't intercept HTTP calls that are initiated on page load (like in most SPAs), as it requires some setup work that can only be done after the page is loaded (due to limitations in selenium). That means you can just capture requests that were initiated inside a test. If you're fine with that, this plugin might be for you, so read on.

Though, I managed to fix it that way:

// Get the setup function from the interceptor service and inline it
let { setup } = require('wdio-intercept-service/lib/interceptor');
setup = setup
    .toString()
    .replace(/\/\*[\s\S]*?\*\/|\/\/.*/gm, '') // remove comments
    .replace(/\n?\r|\n/gm, '') // remove new lines
    .replace(/\s+/gm, ' '); // remove consecutive white spaces

// Listen for any call to the Angular application
const mock = await browser.mock('http://localhost:4200/**');

// Inject the setup as a script if the call was asking for `index.html`
mock.respond(({ body }) => {
    if ((typeof body === 'string') && body.includes('<body>')) {
        return body.replace('<body>', `<body><script>${setup}; setup(function() {});</script>`);
    }
    return body;
});

The idea behind it, is to inject a script in the index.html root file of the Angular application that will set up the interceptor service before the app is actually started.


I let you guys decide whether this could be added directly to the library or wrote somewhere in the documentation 😉

christian-bromann commented 2 years ago

Afaik this package was build before WebdriverIO had native mocking capabilities and continues to stay a great alternative for browser tests where Puppeteer and CDP can't be used. Not sure how much it makes sense to use both together, because e.g.:

browser.expectRequest('GET', /\/api\/foo/, 200);

is the same as:


await expect(mock).toBeRequestedWith({
    url: '*/api/foo',
    method: 'GET',                                 // [optional] string | array
    statusCode: 200
})

If this package has some useful functionality that native WebdriverIO APIs don't have I suggest we port them over. What do you think?

Badisi commented 2 years ago

The thing is, I was never able to make toBeRequestedWith work...

Even this simple example doesn't work:

const url = 'http://localhost:4200';
const mock = browser.mock(url);
await browser.url(url);
await expect(mock).toBeRequestedWith({ url });

Am I doing something wrong ?

christian-bromann commented 2 years ago

Am I doing something wrong ?

Not sure, is it giving you an error message? Are you using latest WebdriverIO?

Badisi commented 2 years ago

wdio version: v7.16.10

spec

describe('Test', () => {
    it('test', async () => {
        const url = 'http://localhost:4200';
        const mock = browser.mock(url);
        await browser.url(url);
        await expect(mock).toBeRequestedWith({ url });
    });
});

result

[chrome 96.0.4664.55 mac os x #0-0] Expect mock to be called with
Expected: {"url": "http://localhost:4200"}
Received: "was not called"