Jest: await mssql.connect not working (timeout) - sql-server

I want to test some functionality that should not rely on mocked sql query results but that should actually write to the database in a nextjs Typescript project.
Here's a minimal example of what is going wrong:
/* #/lib/db.ts */
import sql, { ConnectionPool } from "mssql";
const sqlConfig: sql.config = {
user: process.env.DB_USER,
password: process.env.DB_PWD,
database: process.env.DB_NAME,
server: process.env.DB_HOST!,
pool: {
max: parseInt(process.env.MAX_DB_CONNECTIONS || "10"),
min: 0,
idleTimeoutMillis: 30000
},
options: {
encrypt: true, // for azure
trustServerCertificate: true // change to true for local dev / self-signed certs
}
};
if (!global.db) {
global.db = { pool: null };
}
export async function connectToDatabase(): Promise<ConnectionPool> {
if (!global.db.pool) {
console.log("No pool available, creating new pool."); // this output shows up in the console
const pool = await sql.connect(sqlConfig);
global.db.pool = pool;
console.log("Created new pool."); // this is never reached
}
return global.db.pool!;
}
/* db.test.ts */
import { connectToDatabase } from "#/lib/db";
// as you can see I already tried a high timeout. The db connection never takes this long in development
jest.setTimeout(30000);
describe("Database", ()=>{
it("can connect", async()=>{
const pool = await connectToDatabase();
expect(1).toBe(1);
});
});
export {};
The test above fails because the connectToDatabase() Promise never fulfills (it doesn't reject either, which it should do when the credentials are wrong)
I have copied the .env.local file, which contains the DB credentials, and named it .env.test. I have also verified that the env data are in fact getting read correctly by outputting sqlConfig
The db.ts functions work perfectly in development.
Is there anything I overlooked why this shouldn't work in jest out of the box?

Ok I found it:
After taking a deeper look at the error log, I saw that I only read the lower half (ignore the different file name from the question):
ControllingTableLock
✕ works (30016 ms)
● ControllingTableLock › works
ReferenceError: setImmediate is not defined
at node_modules/mssql/lib/base/connection-pool.js:402:9
at Array.forEach (<anonymous>)
at node_modules/mssql/lib/base/connection-pool.js:401:26
● ControllingTableLock › works
thrown: "Exceeded timeout of 30000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
5 |
6 | describe("ControllingTableLock", ()=>{
> 7 | it("works", async()=>{
| ^
8 | const pool = await connectToDatabase();
9 | expect(1).toBe(1);
10 | return;
After some research I found out that
setImmediate is not defined
is an error that stems from jest using the jsdom environment instead of node.
The fix was very simple: I added
/*
* #jest-environment node
*/
above the imports in the test file.

Related

Playwright & Vite - test block cannot find an element, although exists

I'm running some e2e tests with playwright with vite frontend application.
This is my code:
import { test, expect } from '#playwright/test';
test.describe('Auth page #auth-page', () => {
test('auth page should have brand logo', async ({ page }) => {
await page.goto('http://localhost:8080/');
const isBrandLogoElementVisible = await page.locator("[data-test-id='auth-brand-logo']").isVisible();
expect(isBrandLogoElementVisible).toBe(true);
});
});
I run this script: playwright test -c ./playwright.config.ts
When I run the test the output is
basic/auth.spec.ts:4:6 › Auth page #auth-page › auth page should have brand logo ==============
Error: expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
7 | const isBrandLogoElementVisible = await page.locator("[data-test-id='auth-brand-logo']").isVisible();
8 |
> 9 | expect(isBrandLogoElementVisible).toBe(true);
| ^
10 | });
11 |
12 | test('auth page should have main header', async ({ page }) => {
But, when I run playwright test -c ./playwright.config.ts --debug it succeeds. I guesss it's because the test waits for my inputs on debug mode and then the element start rendering.
How can I change it to work?
I Expect Playwright to find the element as it exists
According to the docs, page.locator(...).isVisible() does not wait for the element to become visible. The recommended way to do this is to use expect(page.locator(...)).toBeVisible() which does wait for a specific, configurable amount of time.
await expect(page.locator("[data-test-id='auth-brand-logo']")).toBeVisible()

Unit Tests Failing After Updating React Scripts from v.3.4.4 to v.4.0.3

I'm working on a new project for a client and have been asked to update create-react-app (react-scripts) from v.2.0.5 to v.4.0.3. I did that, and a bunch of unit tests failed. I went back through the project and isolated the breaking change to be the update from react-scripts 3.4.4 to 4.0.0.
Basically, the main error I'm seeing seems to apply to any tests running against async/await methods. Jest reports that the test times out but these tests are only timing out because they are failing. They all pass without issue on an earlier version of react-scripts (and by extension an earlier version of Jest I'd guess).
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
The timeout seems to be relating to an issue with promises as it's preceeded by this error:
TypeError: Cannot read property 'then' of undefined
Along with a fairly indecipherable stack trace that lists a load of node modules.
Here's an example of the test that's failing:
test('dispatching fetchPublishedArticlesAuthors action causes an API GET and updates the store', async (done) => {
const options = { page: 0, size: 20, sort: 'createdBy.firstName,asc' };
store.dispatch(fetchPublishedArticlesAuthors(options));
const state = await stateChange(store);
expect(fetch).toHaveBeenCalledWith(
`https://content.onehub.test/articles/authors?page=0&size=20&sort=createdBy.firstName%2Casc`,
expect.objectContaining({ method: 'GET' }),
);
expect(getPublishedArticlesAuthors(state)).toMatchSnapshot();
done();
});
The await stateChange(store) references this method:
export function stateChange(store) {
return new Promise((resolve) => {
let currentState = store.getState();
store.subscribe(() => {
const newState = store.getState();
if (newState !== currentState) {
resolve(newState);
}
currentState = newState;
});
});
}
This method works without issue on another project running react-scripts 4.0.3 so I don't think that it's the issue per se but it is failing to return anything if I'm not mistaken.
Store is mocked as follows:
beforeEach(() => {
store = mockStore({ initialState, reducers, rootSaga: sagas });
fetch.mockClear();
});
Any tips on where to start? Confused as to why it fails on a more recent version but passes on an earlier one.
The solution was to set resetMocks to false. I tried doing this in Jest config but couldn't get it working so just added it directly to package.json:
"jest": {
"resetMocks": false
}
It wasn't obvious from the failing tests but having read the release notes you can see that the version of Jest used by Create React App changed from v24 to v26. In the process, resetMocks is set to true which was breaking the implementation in the test above, causing them all to time out.

Cypress: unit testing the pure React component is throwing error

I am writing unit tests using Cypress for my React JS web application. I can write functional tests using Cypress. But I also need to write the unit tests for my presentation components in my app. So I am using this Cypress wrapper for React, https://github.com/cypress-io/cypress/tree/master/npm/react. But, when I write the unit test for my component, it is throwing an error. Following is how I installed and wrote the unit test for my pure component.
First I installed the required libraries running the following command.
npm install --save-dev cypress #cypress/react #cypress/webpack-dev-server
Then I added the following line into the cypress/plugins/index.js
module.exports = (on, config) => {
require('#cypress/react/plugins/react-scripts')(on, config)
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
Then I created a test file called, no_result.spec.js with the following code.
import React from 'react';
import { mount } from '#cypress/react';
import NoResult from "../../../src/components/NoResult";
describe("NoResult Component", () => {
it ('should display message', () => {
mount(<NoResult message={"No data available."} />);
cy.contains('No data available').should('be.visible');
})
})
Then I opened the cypress and run that no_result.js test and I got the following error.
AssertionError
Timed out retrying after 4000ms: Expected to find element: #__cy_root, but never found it.
Because this error occurred during a before each hook we are skipping all of the remaining tests.
node_modules/#cypress/react/dist/cypress-react.esm-bundler.js:317:1
315 | return cy.then(function () {
316 | var selector = "#" + ROOT_ID;
> 317 | return cy.get(selector, { log: false }).then(function ($el) {
| ^
318 | var wasUnmounted = unmountComponentAtNode($el[0]);
319 | if (wasUnmounted && options.log) {
320 | cy.log('Unmounted component at', $el);
What is missing in my configuration and how can I fix it?

Webview context doesn't show up anymore when testing using Appium

I am creating an automation test using Appium and webdriverio:
const wdio = require("webdriverio");
const opts = {
path: "/wd/hub",
port: 4723,
capabilities: {
platformName: "Android",
platformVersion: "11",
deviceName: "Android Emulator",
app: "/path/to/myapk.apk",
automationName: "UiAutomator2",
autoGrantPermissions: true
}
};
async function main() {
const driver = await wdio.remote(opts);
const contexts = await driver.getContexts();
console.log("Contexts:", contexts);
await driver.deleteSession();
}
main();
The problem
When running tests I could see that I used to have two contexts:
NATIVE_APP
WEBVIEW_chrome (or similar, I do not remember exactly the value here)
I then made a change which switched contexts to the webview, there I got an error about the chrome driver not being found. That is when I installed it: npm install "appium-chromedriver".
I do not know if this is what made everything go babanas, but since then, everytime I test, I can only see the native context, no more webview context :(
More info
It is important to point out that I have modified my Android app to include this:
#Override
public void onCreate() {
super.onCreate();
WebView.setWebContentsDebuggingEnabled(true);
}
I can also start chrome://inspect and see the webview is there and even inspect it. But when running tests, the driver cannot see the webview context.
Why? How to fix this?
Turns out that I need to wait for a webview to show up in the app, so this works:
async function main() {
const driver = await wdio.remote(opts);
// Wait a few seconds so the webview properly loads
await new Promise(resolve => setTimeout(resolve, 5000));
const contexts = await driver.getContexts();
console.log("Contexts:", contexts);
await driver.deleteSession();
}

importScripts in Web Workers is undefined inside a React/Webpack environment

I am working on a React app created with create-react-app. I was having trouble creating a web worker in it so I posted a question here on SO: Creating a web worker inside React
I've found a solution, as written in the post above, to load a worker without ejecting the app and messing with the Webpack config. This is the code, from the post above:
// worker.js
const workercode = () => {
self.onmessage = function(e) {
console.log('Message received from main script');
const workerResult = 'Received from main: ' + (e.data);
console.log('Posting message back to main script');
self.postMessage(workerResult);
}
};
let code = workercode.toString();
code = code.substring(code.indexOf("{")+1, code.lastIndexOf("}"));
const blob = new Blob([code], {type: "application/javascript"});
const worker_script = URL.createObjectURL(blob);
module.exports = worker_script;
and in the file that uses the worker:
import worker_script from './worker';
const myWorker = new Worker(worker_script);
myWorker.onmessage = (m) => {
console.log("msg from worker: ", m.data);
};
myWorker.postMessage('im from main');
It works, however, I cannot seem to get importScripts to work. Even if I do this (outside onmessage or inside onmessage):
if (typeof importScripts === 'function') {
importScripts('myscript.js');
}
In that case, the if statement turns out to be true, but then fails on the actual import with the same error message 'importScripts' is not defined as if the if statement is a false positive, which doesn't sound right. I'd say this is a context issue and that the worker probably isn't loading properly (although it seems to work), but it's just a guess.
Any ideas what's happening here?
importScripts in a worker created from Blob works fine, at least in 2021 (react 17.0.2, react-scripts 4.0.3, Chrome 92). The imported script URL must be absolute because worker was created from Blob.
The original issue might have been a bug in webpack or the transpilation might have changed the code in a weird way.
const workercode = () => {
importScripts("https://example.com/extra.js");
console.log(self.extraValue); // 10
self.onmessage = function(e) {
console.log('Message received from main script');
...
}
};
 
// extra.js
self.extraValue = 10;
Looks like this is still broken in 2022 - Seems there is a regression coming down the dev pipeline (at least in Android WebView and possibly some dev/canary chrome verions.)
https://bugs.chromium.org/p/chromium/issues/detail?id=1078821

Resources