Skip to content

Commit

Permalink
Add a local test-utils package to make it easier to share test utilit…
Browse files Browse the repository at this point in the history
…ies and jest defaults, move InlineActionsButtons.spec.tsx to its own feature project
  • Loading branch information
volkanceylan committed Oct 9, 2024
1 parent cca6d68 commit 3fdde6f
Show file tree
Hide file tree
Showing 14 changed files with 385 additions and 11 deletions.
1 change: 1 addition & 0 deletions build/test-utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./mocks";
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,19 @@ const serenityNodeModules = resolve(join(configRoot, "../../../Serenity"));
export default () => ({
coveragePathIgnorePatterns: [
"<rootDir>/node_modules/",
"<rootDir>/src/mocks/",
"/src/Serenity.Assets/"
],
extensionsToTreatAsEsm: ['.ts', '.tsx'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/Modules/$1',
"^@serenity-is/(.*)$": ["<rootDir>/node_modules/@serenity-is/$1", "<rootDir>/../node_modules/@serenity-is/$1", "<rootDir>/../../node_modules/@serenity-is/$1"]

},
setupFiles: [
`${configRoot}/jest-setup.mjs`,
`${configRoot}/jest-setup.js`,
],
setupFilesAfterEnv: [
`${configRoot}/jest-setup-afterenv.mjs`
`${configRoot}/jest-setup-afterenv.js`
],
testEnvironment: `${configRoot}/jest-jsdom-global.mjs`,
testEnvironment: `${configRoot}/jsdom-global.js`,
testMatch: [
"<rootDir>/test/**/*.spec.ts*",
"<rootDir>/src/**/*.spec.ts*"
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
//https://github.com/simon360/jest-environment-jsdom-global
import pkg from "../../../Serenity/node_modules/jest-environment-jsdom/build/index.js";

let JSDOMEnvironment = pkg;
if (pkg.default)
JSDOMEnvironment = pkg.default;
const JSDOMEnvironment = pkg.default || pkg.JSDOMEnvironment || pkg;

export default class JSDOMEnvironmentGlobal extends JSDOMEnvironment {
async setup() {
Expand Down
236 changes: 236 additions & 0 deletions build/test-utils/mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import { ScriptData, peekScriptData, setScriptData } from "@serenity-is/corelib";

let orgEnsure: any;

export function mockDynamicData() {

if (orgEnsure)
return;

orgEnsure = ScriptData.ensure;
ScriptData.ensure = function(name: string, dynJS?: boolean) {

if (peekScriptData(name) == null) {
try {
var data = jest.requireActual("test-utils/dynamic-data/" + name + ".json");
setScriptData(name, data);
}
catch(e) {
console.warn(e);
}
}

return orgEnsure(name, dynJS);
}
}

export function unmockDynamicData() {
if (!orgEnsure)
return;

ScriptData.ensure = orgEnsure;
}

import { resolveServiceUrl } from "@serenity-is/corelib";

export type MockFetchInfo = {
url: string;
init: RequestInit | BodyInit;
data: any;
status?: number;
statusText?: string;
aborted?: boolean;
responseHeaders: Record<string, string>;
}

var orgFetch: any;
var fetchSpy: jest.Mock<Promise<any>, [url: string, init: RequestInit], any> & { requests: MockFetchInfo[] }
var fetchMap: Record<string, (info: MockFetchInfo) => any> = {};

export function mockFetch(map?: { [urlOrService: string]: ((info: MockFetchInfo) => any) }) {
if (!fetchSpy) {
orgFetch = (window as any).fetch;
fetchSpy = (window as any).fetch = jest.fn(async (url: string, init: RequestInit) => {
var callback = fetchMap[url] ?? fetchMap["*"];
if (!callback) {
console.error(`Fetch is not configured on the mock fetch implementation: (${url})!`);
throw `Fetch is not configured on the mock fetch implementation: (${url})!`;
}

var requestData = typeof init.body == "string" ? JSON.parse(init.body) : null;

var info: MockFetchInfo = {
url: url,
init: init,
data: requestData,
status: 200,
aborted: false,
responseHeaders: {
"content-type": "application/json"
}
}

if (init && init.signal) {
init.signal.addEventListener("abort", () => { info.aborted = true });
}

fetchSpy.requests.push(info);

var responseData = callback(info);
return new JsonResponse(responseData, info);
}) as any;
fetchSpy.requests = [];
}

if (map) {
for (var key of Object.keys(map)) {
var url = key == "*" ? "*" : resolveServiceUrl(key);
fetchMap[url] = map[key];
}
}

mockXHR();

return fetchSpy;
}

export function unmockFetch() {
if (fetchSpy !== null &&
(window as any).fetch === fetchSpy) {
fetchSpy = null;
(window as any).fetch = orgFetch;
}
unmockXHR();
}

class JsonResponse {
constructor(private response: any, private info: MockFetchInfo) {
}

get ok() { return this.info.status >= 200 && this.info.status < 300 }

get headers() {
return ({
get: (x) => {
return this.info.responseHeaders[x?.toLowerCase()];
}
})
}

get status() {
return this.info.status;
}

get statusText() {
return this.info.statusText ?? "";
}

text() {
return typeof this.response === "string" ? this.response : JSON.stringify(this.response);
}

json() {
return this.response;
}
}

var xhrOriginal: any;

class MockXHR {
declare public _info: MockFetchInfo;
declare public _responseData: any;

get status() { return this._info.status ?? 200; }
get statusText() { return this._info.statusText; }
get responseText() { return JSON.stringify(this._responseData) }

getResponseHeader(name: string): string {
return this._info?.responseHeaders[name];
}

open(_method: string, url: string, _async?: boolean): void {
this._info ??= {} as any;
this._info.url = url;
}

abort() {
this._info ??= {} as any;
this._info.aborted = true;
}

send(body?: Document | XMLHttpRequestBodyInit): void {
var url = this._info?.url;
var callback = fetchMap[url] ?? fetchMap["*"];
if (!callback) {
console.error(`URL is not configured on the mock XHR implementation: (${url})!`);
throw `URL is not configured on the mock XHR implementation: (${url})!`;
}

var requestData = typeof body == "string" ? JSON.parse(body) : null;

this._info = {
url: url,
init: body instanceof Document ? null : body,
data: requestData,
status: 200,
aborted: false,
responseHeaders: {
"content-type": "application/json"
}
}

fetchSpy.requests.push(this._info);

this._responseData = callback(this._info);
}

setRequestHeader(name: string, value: string): void {
}
}

function mockXHR() {
xhrOriginal ??= window.XMLHttpRequest;
window.XMLHttpRequest = MockXHR as any;
}

function unmockXHR() {
if (xhrOriginal) {
window["XMLHttpRequest"] = (xhrOriginal ?? window["XMLHttpRequest"]) as any;
xhrOriginal = null;
}
}

export function mockAdmin() {
ScriptData.set("RemoteData.UserData", { Username: "admin", IsAdmin: true });
}

export function mockGridSize() {
if (document.getElementById('mockGridSize'))
return;
var style = document.createElement('style')
style.id = "mockGridSize";
style.innerHTML = `
.grid-container { min-height: 10000px !important; height: 10000px !important; min-width: 10000px !important; width: 10000px !important; }
.slick-header.ui-state-default, .slick-headerrow.ui-state-default, .slick-footerrow.ui-state-default, .slick-group-header.ui-state-default { width: 100%; }
.slick-header-columns, .slick-headerrow-columns, .slick-footerrow-columns, .slick-group-header-columns { position: relative; }
.slick-header-column.ui-state-default, .slick-group-header-column.ui-state-default { position: relative;display: inline-block; height: 16px; line-height: 16px; float: left; }
.slick-footerrow-column.ui-state-default { border-right: 1px solid silver; float: left; line-height: 20px; }
.slick-resizable-handle { position: absolute; font-size: 0.1px; display: block; width: 4px; right: 0px; top: 0; height: 100%; }
.grid-canvas { position: relative; }
.slick-row.ui-widget-content, .slick-row.ui-state-active { position: absolute; width: 100%; }
.slick-cell, .slick-headerrow-column, .slick-footerrow-column { position: absolute; }
.slick-group-toggle { display: inline-block; }
.slick-selection { z-index: 10; position: absolute; }
.slick-pane { position: absolute; outline: 0; width: 100%; }
.slick-pane-header { display: block; }
.slick-header { position: relative; }
.slick-headerrow { position: relative; }
.slick-top-panel-scroller { position: relative; }
.slick-top-panel { width: 10000px }
.slick-viewport { position: relative; outline: 0; width: 10000px !important; height: 10000px !important; }
.slick-row { height: 20px !important; width: 10000px !important; }
.slick-cell { width: 30px !important; height: 20px !important; }
`;

document.head.appendChild(style);
}
4 changes: 4 additions & 0 deletions build/test-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"id": "test-utils",
"type": "module"
}
15 changes: 15 additions & 0 deletions build/test-utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"noEmit": true,
"outDir": "./out",
"typeRoots": [
"../../../Serenity/node_modules/@types",
],
"types": [
"jest"
]
},
"include": [
"."
]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"@serenity-is/corelib": "../Serenity/packages/corelib",
"@serenity-is/sleekgrid": "../Serenity/packages/sleekgrid",
"@serenity-is/tsbuild": "8.7.2",
"jsx-dom": "8.1.5"
"jsx-dom": "8.1.5",
"test-utils": "../common-features/build/test-utils"
},
"scripts": {
"all": "pnpm i && pnpm -r build && pnpm -r tsc && pnpm -r dts"
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions src/Serenity.Demo.BasicSamples/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import jestDefaults from "test-utils/jest-defaults.js";

export default {
...jestDefaults()
};
2 changes: 2 additions & 0 deletions src/Serenity.Demo.BasicSamples/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
"scripts": {
"build": "node ./tsbuild.js",
"build:watch": "node ./tsbuild.js --watch",
"jest": "node ../../../Serenity/node_modules/jest/bin/jest.js",
"test": "node ./tsbuild.js && pnpm jest --coverage",
"tsc": "tsc"
},
"type": "module"
Expand Down
Loading

0 comments on commit 3fdde6f

Please sign in to comment.