Testcafe: Add the capability to execute a custom script before page initialization scripts

Created on 29 Aug 2017  Â·  32Comments  Â·  Source: DevExpress/testcafe

Are you requesting a feature or reporting a bug?

feature suggestion

What is the current behavior?

TestCafe executes a ClientFunction when the page is fully loaded.

What is the expected behavior?

Add ability to execute any client script in the browser before other page scripts.

Here are some use cases:
Catch JS errors and console output
Executing code in the browser before the page code evaluates

Proposed API:

import fs from 'fs';

const initializationScript = fs.readFileSync('./script.js').toString()

fixture `My fixture`
    .addScriptToEachPage(initializationScript);

test
    .addScriptToEachPage(initializationScript);

Feel free to vote for the feature if you consider it useful and please let us know about your use case for it.

Auto-locked enhancement

Most helpful comment

This would also be helpful if certain local storage keys need to be set before the page loads.

All 32 comments

This feature is useful e.g. if you want to override console.log to spy on console output in your tests.

Yes. This feature is good

@AlexanderMoskovkin

I think that ClientFunction support will be useful for this feature:

test.addScriptToEachPage(ClientFunction(...));

I would suggest to extend the proposal with several points made in #2234:

  • Multiple asset types (scripts, styles, fonts), useful for UI/visual regression testing or any cases that would require to apply test-related changes to application code otherwise

  • Multiple asset insertion positions (start and end of head and body), with end of document body as default value

  • 'Here and now' asset insertion position for fixture/test assets, a shortcut for t.eval () => { document.createElement... }, also accepts a ClientFunction

  • Local assets that are resolved from NPM module name or relative path, a shortcut for fs.readFileSync('require.resolve('module'), 'utf8') (currently included as is, no bundling or transpilation)

  • Remote CDN assets identified by URL

  • Fixture assets

  • Test assets

  • Global assets for entire test run (similar to Karma files) as runner and CLI options

Currently the framework has undocumented embeddingOptions runner method, it makes an asset (script or style) being served by TestCafe server, is available only on runner init but not in CLI. Another part of the process is internal testRun.injectable property that allows to push served script/style to <head>, it seems to be reachable in specs as t.testRun.injectable but is unavailable on runner init.

This would also be helpful if certain local storage keys need to be set before the page loads.

Note: we already have build-in support for accessing console messages

any update for this enhancement?

Hi @juliusgonawan

Would you please describe a real life case for which you need this functionality?

@miherlosev
A real world escenario i've found is avoiding a service worker registration. If a service worker is registered without using https, chrome logs a console error which forces you to run testcafe with the -e flag. I'd like to run testcafe without ignoring Js Error messages.

Service worker are usually registered with an inline script:

<script>
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js');
}
</script>

So if i try to mock navigator.serviceWorker in the test, it will be mocked after the script has run.

@miherlosev
A real world scenario is mine. I want to use PollyJS to record/mock some xhr or fetch requests. But to do so, I have to inject it into the page early. I tried it with ClientFunction, but it injects too late, so it missed some requests. An option to inject code as early as possible would be fine.

@NickCis, @bitjoo - thank you for providing additional details. You can solve these issues by using RequestMock to handle specific page requests. Take a look at the following topics in the documentation:

For example, you can mock a service worker script for browsers that don't support this feature using the following code:

import { ClientFunction, RequestMock } from 'testcafe';

const supportsServiceWorkers = ClientFunction(() => 'serviceWorker' in navigator);

fixture `ServiceWorker`
    .page('http://my-site.com')
    .beforeEach(async t => {
         const canUseServiceWorkers = await supportsServiceWorkers();

         if (canUseServiceWorkers)
             return;

         await t.addRequestMocks(
             RequestMock()
                 .onRequest('http://my-site.com/service-worker.js')
                 .respond('');
         );
     });

@miherlosev One more use case is that. Since client scripts are loading after page load, we can not mock any setInterval, setTimout functions

I think this issue fits in my case as well, as I would like to use axe-core within my tests - and as this requires the library to be run within browser client, i think it fits. I can see that it's now in the roadmap which is great, but perhaps there could be some neat workaround to achieve it? I wouldn't like to hack in a way that I'll store the dist of the axe-core somewhere, then load it in the runtime, hope it loads and then run a test 🤔

@Worie, I guess you can use axe-core with TestCafe without the suggested feature. Please check a demo project made by @helen-dikareva, which integrates axe with TestCafe: https://github.com/helen-dikareva/axe-testcafe

I would also love to have this ability, especially to be able to use normal npm modules that use module.exports' orexport` to expose functions etc.

Also should be possible to specify an initialization script on the test run level (see https://github.com/DevExpress/testcafe/issues/3857).

const runner = testcafe.createRunner();
        return runner
            .src(['tests/fixture1.js', 'tests/func/fixture3.js'])
            .browsers(['chrome', 'safari'])
            .addScriptToEachPage(initializationScript)
            .run();

Don't you think we should be able to do this without having to create a custom runner script?

Could you please describe your test case in greater detail? We need this information to design our API as much flexible as possible.

@helen-dikareva if you look at @miherlosev's example above, the other options (src, browser, etc) are all options you can set in the testcafe config file. What I'm saying, is that we should also be able to specify the addScriptToEachPage in the config as well, so we can continue using the config file (which is lovely).

-Ben

@benmonro
Thank you for the description. If we decide to implement @miherlosev's suggestion we will consider your suggestion as well.

@AlexKamaev would you guys accept a PR if I implement it?

@benmonro, you are welcome to submit your PR. We will be happy to review it.

hey all, started this PR w/ the changes: https://github.com/DevExpress/testcafe/pull/3880
a bit unclear about where the tests for this should go, but hopefully the PR makes sense.

@benmonro

We are discussing the API with the team. We'll prepare our full vision of the issue within a few days.
In addition, I want to mention that we'd like to support this feature not only for a configuration file, but for runner (as @miherlosev suggested in the https://github.com/DevExpress/testcafe/issues/1739#issuecomment-498249475), and fixture/test (as @AlexanderMoskovkin suggested in https://github.com/DevExpress/testcafe/issues/1739#issue-253686497)

After discussing with the team we introduce the following API principles for adding custom scripts to the tested page:

  • should be possible to add a custom script on several framework levels: Test API, Command line, Configuration and Runner
  • should be possible to specify one, list or array of the added custom scripts
  • should be possible to add a custom script to all or separate pages
  • should be possible to specify path or content of the added script

Client script initializers

String

fixture `Fixture`
    .clientScripts('asserts/helpers.js'); // path to file (relative or absolute)

Object with content

fixture `Fixture`
    .clientScripts({ content: 'var i = 0' '});

Object with path

fixture `Fixture`
     .clientScripts({ path: './assets/helpers.js' });

Object with page property (specified the pages for which the target script will be injected)

fixture `Fixture`
    .clientScripts({
        path: 'assets/helper.js'
        page: 'http://example.com'
    })

Object with 'module' property

fixture `Fixture`
    .clientScripts({ module: 'dom-utils' })

Possible arguments

fixutre `Fixture`.clientScripts('test1.js'); // single argument
fixture `Fixture.clientScripts('test1.js', 'test2.js'); // list of arguments
fixture `Fixture.clientScripts(['test1.js']); // array of arguments

API:

Test and fixture

fixture `My fixture`.clientScripts('test.js');
test.clientScripts('test.js');

Command line

testcafe chrome /tests --client-scripts asserts/jquery.js,mockDate.js

Configuration

{
    "clientScripts": ['wait-for-react-helper.js']
}

Runner

const runner = testcafe.createRunner();
        return runner
            .src(['tests/fixture1.js', 'tests/func/fixture3.js'])
            .browsers(['chrome', 'safari'])
            .clientScripts(['/assets/jquery.js'])
            .run();    

Would you also please consider a separate configuration for "beforeEach" that doesn't need to be set in each fixture? I had originally requested that and that issue was closed in favor of this one. Jest and mocha and other test runners have this feature as well for things like testing library add ons (like testcafe-testing-library) and it adds a nice way to centrally configure those things

Would you also please consider a separate configuration for "beforeEach" that doesn't need to be set in each fixture?

If you want to add a custom script for each fixture, use the clientScripts method/property at the Configuration, Command line and Runner levels.

For example, the following code will add /assets/jquery.js content to the tested page in each fixture.

const runner = testcafe.createRunner();
        return runner
            .src(['tests/fixture1.js', 'tests/func/fixture3.js'])
            .browsers(['chrome', 'safari'])
            .clientScripts(['/assets/jquery.js'])
            .run();    

Yes but what if it's not a client script? Ie something I just want to set
up in the test side

On Thu, Jun 13, 2019 at 5:30 AM Mikhail Losev notifications@github.com
wrote:

Would you also please consider a separate configuration for "beforeEach"
that doesn't need to be set in each fixture?

If you want to add a custom script for each fixture, use the clientScripts
method/property at the Configuration, Command line and Runner levels.

For example, the following code will add /assets/jquery.js content to the
tested page in each fixture.

const runner = testcafe.createRunner();
return runner
.src(['tests/fixture1.js', 'tests/func/fixture3.js'])
.browsers(['chrome', 'safari'])
.clientScripts(['/assets/jquery.js'])
.run();

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/DevExpress/testcafe/issues/1739?email_source=notifications&email_token=AADBPBAQ757VIVZ7IWO7B6DP2I4WDA5CNFSM4DYYFYX2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODXTQ66Q#issuecomment-501682042,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AADBPBF5O7F6FA3TWAE6QFDP2I4WDANCNFSM4DYYFYXQ
.

>

Ben Monro
Software Developer

@benmonro
In this issue, we are discussing the capability to execute a custom script on the client side before page initialization scripts.
If you want to set up something on the server side, use the fixture.before hook.
If you have other questions that are not connected with the issue's subject, please create a separate issue regarding them. It will allow us to work on the problems and features more efficient.

@miherlosev yes I did that but you closed that issue in favor of this one. https://github.com/DevExpress/testcafe/issues/3857

fixture.before would still require a modification in each test figure file. I would like a way to globally set fixture.before I'm the way they jest or mocha let you set that as well.

@benmonro
We already have a suggestion to implement similar functionality. Please refer to it https://github.com/DevExpress/testcafe/issues/745.
You are also welcome to share your thoughts there. I'd like to note that we do not know when we will be able to start working on that feature.
As for this ticket, let's continue discussing only the custom client script feature here.

This thread has been automatically locked since it is closed and there has not been any recent activity. Please open a new issue for related bugs or feature requests. We recommend you ask TestCafe API, usage and configuration inquiries on StackOverflow.

Was this page helpful?
0 / 5 - 0 ratings