TypeScript and CoffeeScript
The testcafe documentation says that no additional settings are needed to use ES modules when writing tests, however, it's not clear how to configure the testcafe configuration if the project uses ES modules, for example to write global hooks. Because it looks line you have only 2 options to config testcafe globaly: .json and CommomJS
I need authorization before each test in the project, and I have this function for that:
import { Role, Selector, t } from 'testcafe';
export const user = Role('http://localhost:3000/login', async t => {
await t
.typeText(Selector('#loginInput'), 'Login')
.typeText(Selector('#passwordInput'), 'Password')
.click(Selector('button').withAttribute('data-testid', 'submitButton'));
});
And I have tried this in .testcafers.js file
import { user } from './src/testing/utilities/loginUser';
export default {
hooks: {
testRun: {
before: async () => {
await t.useRole(user)
}
},
}
};
To summarize, how can I write a global hook for testcafe, if my project is using ES modules
There is an exception for the config file. You should use CommonJS syntax as described in the documentation.
const { user } = require('./src/testing/utilities/loginUser');
module.exports = {
hooks: {
testRun: {
before: async () => {
await t.useRole(user)
}
},
}
};
Related
I have a test:
import { convertHeicToPng } from './heicUtils';
class Worker {
url: string;
onmessage: (m?: any) => void;
constructor(stringUrl: string) {
this.url = stringUrl;
this.onmessage = () => {};
}
postMessage(msg: any) {
this.onmessage(msg);
}
}
(window.Worker as any) = Worker;
describe('test heicUtils', () => {
test('should convert HEIC to PNG', async () => {
const file = new File([''], 'test.heic', { type: 'image/heic' });
const base64 = await convertHeicToPng(file);
expect(base64).toContain('data:image/png;base64');
});
});
and in heicUtils, I'm using heic2any, which uses WebWorkers. How can I properly mock a Worker for a Jest test?
Since you are testing your heicUtils module, you should mock the heic2any lib, otherwise, you will be testing the 3rd party library instead of your own code.
In the mock, you should define the functions/methods of heic2any that your heicUtils use and what they should return for each test case you intend to write.
Examples of how to mock modules can be found here: https://jestjs.io/docs/manual-mocks
tsc && mocha --reporter spec -t 5000 --exit
.npm install mocha
.then do this cmd
.Examples my github : https://github.com/www778878net/koa78-base78
I'm trying to write some Firestore operations into a separate package so that it could be imported and reused in different web apps. I'm building a monorepo with different packages and I'm trying to use Firebase v9 for the following example:
From packageA I'm defining and exporting a getPosts(db) function that takes in a Firestore object and returns some posts form the given database
// in 'packageA'
import { collection, getDocs, Firestore } from 'firebase/firestore';
export const getPosts = async (db: Firestore) => {
console.log('Passed in db: ', db); // This correctly prints the passed in Firestore object
try {
const postsCollection = collection(db, 'posts'); // This function will throw
const querySnapshot = await getDocs(postsCollection);
return querySnapshot.docs.map((doc) => doc.data());
} catch (e) {
console.error('Error reading posts: ', e);
}
}
In a web app I'm initialising the Firebase app and exporting the Firestore instance
// firebase.js in 'web-app-1'
import { initializeApp } from 'firebase/app';
import { getFirestore } from 'firebase/firestore';
const firebaseConfig = { /* my Firebase config */ };
export const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
Then I'm trying to use the getPosts function from the package in a component...
// App.js in 'web-app-1'
import { db } from './firebase.js';
import { getPosts } from 'packageA';
let posts;
async function loadPosts() {
try {
posts = await getPosts(db);
} catch (e) {
console.error(e);
}
}
loadPosts(); // throws an error
but I get the following error from the collection(db, 'posts') call
Error reading posts: Expected first argument to collection() to be a CollectionReference, a DocumentReference or FirebaseFirestore
even though the passed in database is correctly printed in the console (form the getPosts function)
Note: If I copy the whole getPosts function and use it directly in the web app (i.e. without importing it from another package) then it works and correctly fetches the posts.
It looks like a bug with version 9, and the method is trying to use a Firebase Realtime Database instead of Firestore, so the method is sending an error for the collections.
It seems to override the fact that it's Firestore when using the function, so I would send this to the Firebase support directly because the way that the package is being formed seems to be the main issue.
I've been looking around a bit more and found this answer to a similar question to solve my problem too.
Basically what I had to do is to specify Firebase as a peerDependency in packageA and not include it in the final bundle. The web apps that consume packageA will include Firebase as a regular dependency.
So the package.json files look as follows
In the utility package
{
"name": "packageA",
"peerDependencies": {
"firebase": "^9.6.3"
}
}
and then in the web apps
{
"name": "web-app-1",
"dependencies": {
"firebase": "^9.6.3",
}
}
This approach also makes sense to my use case as the web app – and only the web app – that initialises the Firebase app will include it in its bundle. I can imagine however that in some other use cases this is not a possible solution.
Nevertheless I have submitted my issue to the Firebase support as suggested and here is their answer:
We have received some similar cases and we are already working to solve this. However, it can take a while due the workload of the engineering team, please, be patient.
I am currently experience the same problem. The workaround is importing the files direct via the bundler.
Keep in mind this is not optimal because I have to install the packages in the native project again, so it requires some manual maintenance
Project structure
apps
native
web
packages
utils
This ensures that my app uses the firebase instance and package that is inside native/node_modules/
Metro.config.js
`
const { getDefaultConfig } = require("#expo/metro-config");
const path = require("path");
const projectRoot = __dirname;
const workspaceRoot = path.resolve(__dirname, "../..");
const config = getDefaultConfig(__dirname);
const extraNodeModules = {
'#aim/utils': path.resolve(__dirname + '/../../packages/utils'),
};
const watchFolders = [
path.resolve(__dirname + '/../../packages/utils')
];
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPath = [
path.resolve(projectRoot, "node_modules"),
path.resolve(workspaceRoot, "node_modules"),
];
module.exports = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: false,
},
}),
},
resolver: {
extraNodeModules: new Proxy(extraNodeModules, {
get: (target, name) =>
//redirects dependencies referenced from common/ to local node_modules
name in target ? target[name] : path.join(process.cwd(), `node_modules/${name}`),
}),
},
watchFolders,
};
// module.exports = config;
`
getting types to work (native)
tsconfig.json
`
{
"compilerOptions": {
"allowSyntheticDefaultImports": true,
"jsx": "react-native",
"lib": ["dom", "esnext"],
"moduleResolution": "node",
"noEmit": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"strict": true,
"baseUrl": ".",
"paths": {
"#aim/utils/*": ["../../packages/utils/*"]
}
},
}
`
My plugin, env.js:
export default async (_ctx, inject) => {
const resp = await fetch('/config.json')
const result = await resp.json()
inject('env', result)
// eslint-disable-next-line no-console
console.log('env injected', result)
return result
}
Then an idea was to use it's data inside nuxt.config.js to inject into publicRuntimeConfig:
import env from './plugins/env.js'
publicRuntimeConfig: {
test: env,
},
Then in a browser console i'm checking it:
this.$nuxt.$config
It shows me:
instead of a value, though this.$nuxt.$env shows the correct values:
What's wrong?
UPDATE 1
Tried Tony's suggestion:
// nuxt.config.js
import axios from 'axios'
export default async () => {
const resp = await axios.get('/config.json')
const config = resp.data
return {
publicRuntimeConfig: {
config
}
}
}
It cannot fetch config.json, but if i point it to an external resource: "https://api.openbrewerydb.org/breweries" it does work.
Intention of this question, is to have config.json where a user could simply change variable values there (from a compiled code) and change endpoints without a re-build process.
In nuxt.config.js, your env variable is a JavaScript module, where the default export is the function intended to be automatically run by Nuxt in a plugin's context. Importing the plugin script does not automatically execute that function. Even if you manually ran that function, it wouldn't make sense to use an injected prop as a runtime config because the data is already available as an injected prop.
If you just want to expose config.json as a runtime config instead of an injected prop, move the code from the plugin into an async configuration:
// nuxt.config.js
export default async () => {
const resp = await fetch('/config.json')
const config = await resp.json()
return {
publicRuntimeConfig: {
keycloak: config
}
}
}
I'm trying to switch from Mocha and Chai to Jest. In my current setup I'm also using chai-files to compare the contents of two files:
import chai, { expect } from 'chai';
import chaiFiles, { file } from 'chai-files';
import fs from 'fs-extra';
import { exec } from 'child-process-promise';
chai.use(chaiFiles);
describe('cli', () => {
before(() => {
process.chdir(__dirname);
});
it('should run', async () => {
// make a copy of entry file
fs.copySync('./configs/entry/config.version-and-build.xml', './config.xml');
// executes code that changes temp files
await exec('../dist/cli.js -v 2.4.9 -b 86');
// checks if target file and change temp file are equal
expect(file('./config.xml')).to.equal(file('./configs/expected/config.version-and-build.to.version-and-build.xml'));
});
afterEach(() => {
if (fs.existsSync(tempConfigFile)) {
fs.removeSync(tempConfigFile);
}
});
});
How should this be done in Jest? Will I need to load both files and compare the content?
Yes, simply load the contents of each like so:
expect(fs.readFileSync(actualPath)).toEqual(fs.readFileSync(expectedPath));
How to mock not installed npm package in jest?
I'm writing a library and I need to test some cases when optional dependencies are not installed.
Update
My library has an optional dependency. The end-user of my library can optionally to install styled-components.
In my tests (jest) I covered the case when styled-components is installed.
Now I need to cover the case when the package is not installed.
test(`When styled-components is not installed`, () => {
process.env.SC_NOT_INSTALLED = true
const fn = () => {
const styled = require(`./styled`)
}
expect(fn).toThrow(Error)
})
let styled
try {
require.resolve(`styled-components`)
styled = require(`styled-components`)
if (process.env.NODE_ENV === `test` && process.env.SC_NOT_INSTALLED) {
throw new Error(`Imitation styled-components is not installed`)
}
}
catch {
styled = () => {
throw new Error(`Module not found: styled-components`)
}
}
export default styled
process.env.SC_NOT_INSTALLED -> will not work because as I guess the test are running in different process.
When an exception is thrown in your try you are exporting a function.
Calling the exported function is what throws the Error.
Change your test to this:
test(`When styled-components is not installed`, () => {
process.env.SC_NOT_INSTALLED = true;
const styled = require(`./styled`).default;
expect(() => styled()).toThrow('Module not found: styled-components'); // Success!
});
...and it should work.
Update
If you are calling require('./styled') multiple times in the same test file, then you will want to add an afterEach that calls jest.resetModules, otherwise Jest will cache the module and just keep returning the same module for each require:
afterEach(() => {
jest.resetModules();
})
test(`When styled-components is installed`, () => {
const styled = require(`./styled`).default;
// ...
});
test(`When styled-components is not installed`, () => {
process.env.SC_NOT_INSTALLED = true;
const styled = require(`./styled`).default;
expect(() => styled()).toThrow('Module not found: styled-components'); // Success!
});