Object destructuring assignment works with Node but not with Jest - javascript

'Using the Playwright Library [1.21.0] with Jest [27.5.1] to run browser automation tests on Node.js [14.18.2].
ESM is the current module format.
// package.json
...
"type": "module",
...
I setup a quick test with the following code:
// playwright-hello.js
import {chromium} from 'playwright';
async function main() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('http://www.example.com');
await page.screenshot({ path: 'screenshot.png' });
await browser.close();
}
main().catch(console.error);
The above code works via CLI command node playwright-hello with no issues.
However, when I try to run similar code with Jest, I get the following error:
FAIL test/index.test.js
● Test suite failed to run
SyntaxError: The requested module 'playwright' does not provide an export named 'chromium'
at Runtime.linkAndEvaluateModule (node_modules/jest-runtime/build/index.js:779:5)
at TestScheduler.scheduleTests (node_modules/#jest/core/build/TestScheduler.js:333:13)
at runJest (node_modules/#jest/core/build/runJest.js:404:19)
at _run10000 (node_modules/#jest/core/build/cli/index.js:320:7)
at runCLI (node_modules/#jest/core/build/cli/index.js:173:3)
I fixed this by updating the test code to use the default export instead:
// changed to use default export instead of named one
import playwright from 'playwright';
// prepended `playwright.` to initial `chromium.launch()`
const browser = await playwright.chromium.launch();
How come the object destructuring assignment in playwright-hello.js [i.e. import {chromium}...] works with node.js CLI, but not with Jest? In other words, node.js did not complain about playwright library not providing a named export, why?
Note: I'm running Jest as follows for ES6 modules support:
NODE_OPTIONS='--experimental-vm-modules' npx jest ./test/index

Related

In jasmine + karmajs, using global await make the test fail

Using karma and jasmine...
I have a module using a global await:
import ...
const data = await fetch()...
And a test using it:
import ...
describe() ...
It seems that jasmine complain about it:
An error was thrown in afterAll
Error: 'describe' should only be used in 'describe' function in node_modules/jasmine-core/lib/jasmine-core/jasmine.js (line 1811)
Any idea if my analysis is correct?
Any idea on how to configure karma to make that work?

Jest Test Error: browserType.launch: setImmediate is not defined. While using Playwright

I am getting an error while testing with jest in my Javascript code.
The Error:
FAIL src/test/testPlaywright.test.jsx (5.779 s)
× Creating a Snapshot of interacting with playwright (149 ms)
● Creating a Snapshot of interacting with playwright
browserType.launch: setImmediate is not defined
4 | for(const browserType of [chromium, firefox, webkit]){
5 | console.log("Testing");
> 6 | const browser = await browserType.launch({headless: false});
| ^
7 | const page = await browser.newPage();
8 | await page.setDefaultNavigationTimeout(1000000);
9 | await page.goto('https://app.wien.gv.at/navigation/', { waitUntil: 'networkidle' });
at Object.<anonymous> (src/test/testPlaywright.test.jsx:6:43)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 8.517 s
Ran all test suites related to changed files.
The Code:
import { chromium, webkit, firefox } from "playwright";
test('Creating a Snapshot of interacting with playwright',async()=>{
for(const browserType of [chromium, firefox, webkit]){
console.log("Testing");
const browser = await browserType.launch({headless: false});
const page = await browser.newPage();
await page.setDefaultNavigationTimeout(1000000);
await page.goto('https://app.wien.gv.at/navigation/', { waitUntil: 'networkidle' });
await page.click("(//mat-icon[text()='keyboard_arrow_down'])[1]")
await page.click("#mat-select-0")
await page.click('//span[#class="mat-option-text"][contains(.,"Veranstaltungszentrum")]')
await page.click("//mat-icon[text()='keyboard_arrow_up']")
await page.click("(//mat-icon[text()='keyboard_arrow_down'])[2]")
await page.click("#mat-select-3")
await page.click('//span[#class="mat-option-text"][contains(.,"Kapelle Krankenhaus Hietzing")]')
await page.waitForTimeout(2000);
await page.screenshot({path:'image-'+browserType.name()+'.png'});
await browser.close();
}
})
The Problem is the setImmediate function.
I just added the polyfill.js in my test class : import 'core-js';
after that setImmediate was defined again and works now as intended.
As of Jest 28 you can set the testEnvironment on a per-file basis. For example, your main jest.config.js can have
testEnvironment: 'jsdom'
but then in an individual file that runs in the Node environment you can put this at the top:
/**
* #jest-environment node
*/
which will allow setImmediate() and other Node-specific things to work.
I suspect your jest.config.js contains
testEnvironment: 'jsdom'
you can try remove this setting or
 testEnvironment: 'node'
I hope you have good luck.
another way to solve it is installing setimmediate
with npm install -D setimmediate and import it before cloudinary
it must look like this
import 'setimmediate';
import cloudinary from 'cloudinary';
The solution that worked well for me, in react-native environment, was the following
run on the terminal:
yarn add -D core-js
and add inside of the jest.config.js file, or the config file configured on the jest parameter setupFiles the following import
import 'core-js';

Mocking node modules with Jest and #std/esm

I'm currently having an issue writing tests for a node application that uses #std/esm. I've setup a manual mock of a node module inside a __mocks__ directory and the following code shows a test for the file uses this mocked node module. (It's used in db.mjs)
const loader = require('#std/esm')(module, { cjs: true, esm: 'js' })
const Db = loader('../src/db').default
const db = new Db()
describe('getNotes', () => {
it('gets mocked note', () => {
db.getNote()
})
})
However, when I run Jest, my manual mock is not being used, it is using the real node module.
Does anyone have any thoughts on why this could be happening?
Jest is particular about the location of your mocks. From their documentation on mocking node modules:
For example, to mock a scoped module called #scope/project-name,
create a file at mocks/#scope/project-name.js, creating the
#scope/ directory accordingly.
In your case it would be __mocks__/#std/esm.js

"fetch is not found globally and no fetcher passed" when using spacejam in meteor

I'm writing unit tests to check my api. Before I merged my git test branch with my dev branch everything was fine, but then I started to get this error:
App running at: http://localhost:4096/
spacejam: meteor is ready
spacejam: spawning phantomjs
phantomjs: Running tests at http://localhost:4096/local using test-in-console
phantomjs: Error: fetch is not found globally and no fetcher passed, to fix pass a fetch for
your environment like https://www.npmjs.com/package/unfetch.
For example:
import fetch from 'unfetch';
import { createHttpLink } from 'apollo-link-http';
const link = createHttpLink({ uri: '/graphql', fetch: fetch });
Here's a part of my api.test.js file:
describe('GraphQL API for users', () => {
before(() => {
StubCollections.add([Meteor.users]);
StubCollections.stub();
});
after(() => {
StubCollections.restore();
});
it('should do the work', () => {
const x = 'hello';
expect(x).to.be.a('string');
});
});
The funniest thing is that I don't even have graphql in my tests (although, I use it in my meteor package)
Unfortunately, I didn't to find enough information (apart from apollo-link-http docs that has examples, but still puzzles me). I did try to use that example, but it didn't help and I still get the same error
I got the same error importing a npm module doing graphql queries into my React application. The app was compiling but tests were failing since window.fetch is not available in the Node.js runtime.
I solved the problem by installing node-fetch https://www.npmjs.com/package/node-fetch and adding the following declarations to jest.config.js:
const fetch = require('node-fetch')
global.fetch = fetch
global.window = global
global.Headers = fetch.Headers
global.Request = fetch.Request
global.Response = fetch.Response
global.location = { hostname: '' }
Doing so we instruct Jest on how to handle window.fetch when it executes frontend code in the Node.js runtime.
If you're using nodejs do the following:
Install node-fetch
npm install --save node-fetch
Add the line below to index.js:
global.fetch = require('node-fetch');
The problem is this: fetch is defined when you are in the browser, and is available as fetch, or even window.fetch
In the server it is not defined, and either needs to be imported explicity, or a polyfill like https://www.npmjs.com/package/unfetch (as suggested in the error message) needs to be imported by your test code to make the problem go away.

Running tests .mjs / ESM on Node using Jasmine or any other alternative

My Node-based project is implemented using native ES module support on Node thanks to the --experimental-modules CLI switch (i.e. node --experimental-modules).
Obviously, when I run a spec using Jasmine node --experimental-modules ./node_modules/jasmine/bin/jasmine I get the following error:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module
Is it ever possible to use Jasmine using ES modules in Node?
If not, is there any alternative to don't use a framework (e.g. running tests with npm scripts)?
It was easier than I thought.
It's just about calling a file which you might call run.mjs as follows:
node --experimental-modules ./run.mjs
The whole file would look like this:
jasmine.mjs:
import Jasmine from "jasmine"
import JasmineConsoleReporter from "jasmine-console-reporter"
const jasmine = new Jasmine()
jasmine.loadConfigFile( "./support/jasmine.json" )
jasmine.env.clearReporters()
jasmine.addReporter( new JasmineConsoleReporter( {
colors: true,
cleanStack: true,
verbosity: 4,
listStyle: 'indent',
activity: false
} ) )
export default jasmine
And you would add specs as follows in separate files:
import jasmine from './my-project/spec/jasmine.mjs'
jasmine.env.describe('Foo', () => {
jasmine.env.it('Bar', () => {
// Expects, assertions...
})
})
Finally, you would run jasmine importing both configured jasmine instance and specs:
import jasmine from './my-project/spec/jasmine.mjs'
import someSpec1 from './my-project/spec/someSpec1.mjs'
import someSpecN from './my-project/spec/someSpecN.mjs'
someSpec1()
someSpecN()
jasmine.execute()
Simplifying the solution of #Matias_Fidemraizer, keeping only the important bits in one file:
import glob from 'glob';
import Jasmine from 'jasmine';
const jasmine = new Jasmine();
jasmine.loadConfigFile('tests/jasmine.json');
// Load your mjs specs
glob('**/*-test.mjs', function (er, files) {
Promise.all(
files
// Use relative paths
.map(f => f.replace('tests/specs/', './'))
.map(f => import(f)
.catch(e => {
console.error('** Error loading ' + f + ': ');
console.error(e);
process.exit(1);
}))
)
.then(() => jasmine.execute());
});
And execute it with
node --experimental-modules jasmine-run.mjs
You will have some problems in the logs, receiving a message like
[...] .split('\n') [...]
That message mean that you have an exception in the mjs code.
You can follow there: https://github.com/jasmine/jasmine-npm/issues/150

Categories