Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIE-206 App Startup - unit tests pt1 #5246

Merged
merged 4 commits into from
Feb 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/analysis/modals/HailBatchModal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ const defaultAjaxImpl = {
getAppV2: jest.fn(),
};

jest.mock('src/libs/ajax');
jest.mock('src/libs/ajax/leonardo/Apps');

describe('HailBatchModal', () => {
Expand Down
47 changes: 2 additions & 45 deletions src/appLoader.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,5 @@
import 'src/libs/ajax/ajax-override-utils';
import 'src/style.css';

import _ from 'lodash/fp';
import { createRoot } from 'react-dom/client';
import { h } from 'react-hyperscript-helpers';
import RModal from 'react-modal';
import { startPollingServiceAlerts } from 'src/alerts/service-alerts-polling';
import { startPollingVersion } from 'src/alerts/version-polling';
import { initializeAuthListeners, initializeAuthUser } from 'src/auth/app-load/init-auth';
import { initAuthTesting } from 'src/auth/app-load/init-auth-test';
import { initializeAuthMetrics } from 'src/auth/app-load/init-metrics';
import { initializeClientId } from 'src/auth/app-load/initializeClientId';
import { initializeSystemProperties } from 'src/auth/system-loader';
import { setupAjaxTestUtil } from 'src/libs/ajax';
import { isAxeEnabled } from 'src/libs/config';
import Main from 'src/pages/Main';
import { doAppLoad } from 'src/libs/startup/app-loader';

const rootElement = document.getElementById('root');

RModal.defaultStyles = { overlay: {}, content: {} };

window._ = _;

setupAjaxTestUtil();

initializeAuthListeners();
initializeAuthMetrics();
initAuthTesting();

initializeClientId().then(() => {
const root = createRoot(rootElement);
root.render(h(Main));

// react-notifications-component sets up its Store in the componentDidMount method
// of the ReactNotifications component. Use setTimeout to allow that to happen before
// doing anything that may show a notification.
setTimeout(() => {
initializeSystemProperties();
initializeAuthUser();
startPollingServiceAlerts();
startPollingVersion();
}, 0);

if (isAxeEnabled()) {
import('src/libs/axe-core');
}
});
doAppLoad();
16 changes: 15 additions & 1 deletion src/libs/ajax/ajax-override-utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { makeError, makeSuccess, mapJsonBody } from './ajax-override-utils';
import { makeError, makeSuccess, mapJsonBody, mountAjaxOverrideUtils } from './ajax-override-utils';

type FetchFn = typeof fetch;

Expand Down Expand Up @@ -100,3 +100,17 @@ describe('makeSuccess', () => {
expect(responseBody).toEqual({ message: 'Success' });
});
});

describe('mountAjaxOverrideUtils', () => {
it('mounts utils for use by end2end tests', () => {
// Act
mountAjaxOverrideUtils();

// Assert
expect(window.ajaxOverrideUtils).toBeDefined();
expect(window.ajaxOverrideUtils.makeError).toBeDefined();
expect(window.ajaxOverrideUtils.makeSuccess).toBeDefined();
expect(window.ajaxOverrideUtils.mapJsonBody).toBeDefined();
expect(window.ajaxOverrideUtils.overrideAppsWithLocalWDS).toBeDefined();
});
});
4 changes: 3 additions & 1 deletion src/libs/ajax/ajax-override-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,6 @@ declare global {
}
}

window.ajaxOverrideUtils = ajaxOverrideUtils;
export const mountAjaxOverrideUtils = () => {
window.ajaxOverrideUtils = ajaxOverrideUtils;
};
15 changes: 0 additions & 15 deletions src/libs/axe-core.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { asMockedFn, partial } from '@terra-ui-packages/test-utils';
import { Apps, AppsAjaxContract } from 'src/libs/ajax/leonardo/Apps';
import { Runtimes, RuntimesAjaxContract } from 'src/libs/ajax/leonardo/Runtimes';
import { Workspaces, WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces';

import { AjaxTestingContract, setupAjaxTestUtil } from './ajax';
import { Apps, AppsAjaxContract } from './ajax/leonardo/Apps';
import { Runtimes, RuntimesAjaxContract } from './ajax/leonardo/Runtimes';
import { Workspaces, WorkspacesAjaxContract } from './ajax/workspaces/Workspaces';
import { AjaxTestingContract, setupAjaxTestUtil } from './ajax-test-root';

jest.mock('src/libs/ajax/leonardo/Apps');
jest.mock('src/libs/ajax/leonardo/Runtimes');
Expand Down
File renamed without changes.
117 changes: 117 additions & 0 deletions src/libs/startup/app-loader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { delay } from '@terra-ui-packages/core-utils';
import { act } from 'react-dom/test-utils';
import { startPollingServiceAlerts } from 'src/alerts/service-alerts-polling';
import { startPollingVersion } from 'src/alerts/version-polling';
import { initializeAuthListeners, initializeAuthUser } from 'src/auth/app-load/init-auth';
import { initAuthTesting } from 'src/auth/app-load/init-auth-test';
import { initializeAuthMetrics } from 'src/auth/app-load/init-metrics';
import { initializeClientId } from 'src/auth/app-load/initializeClientId';
import { initializeSystemProperties } from 'src/auth/system-loader';
import { mountAjaxOverrideUtils } from 'src/libs/ajax/ajax-override-utils';
import { isAxeEnabled } from 'src/libs/config';
import { setupAjaxTestUtil } from 'src/libs/startup/ajax-test-root';
import { initAxeTools } from 'src/libs/startup/axe-core';
import Main from 'src/pages/Main';
import { asMockedFn } from 'src/testing/test-utils';

import { doAppLoad } from './app-loader';

jest.mock('src/alerts/service-alerts-polling');
jest.mock('src/alerts/version-polling');
jest.mock('src/auth/app-load/init-auth');
jest.mock('src/auth/app-load/init-auth-test');
jest.mock('src/auth/app-load/init-metrics');
jest.mock('src/auth/app-load/initializeClientId');
jest.mock('src/auth/system-loader');
jest.mock('src/libs/ajax/ajax-override-utils');
jest.mock('src/libs/startup/ajax-test-root');
jest.mock('src/libs/startup/axe-core');

// avoid brand colors bootstrapping
jest.mock('src/libs/style');

type ConfigExports = typeof import('src/libs/config');
jest.mock(
'src/libs/config',
(): ConfigExports => ({
...jest.requireActual<ConfigExports>('src/libs/config'),
getConfig: jest.fn(() => ({ brand: 'terra' })),
isAxeEnabled: jest.fn(() => false),
})
);

type MainPageExports = typeof import('src/pages/Main') & {
__esModule: true;
};
jest.mock(
'src/pages/Main',
(): MainPageExports => ({
__esModule: true,
default: jest.fn(),
})
);

describe('doAppLoad', () => {
beforeAll(() => {
asMockedFn(Main).mockReturnValue('Mock Main Page');
asMockedFn(initializeClientId).mockResolvedValue(undefined);
});

beforeEach(() => {
document.body.innerHTML = '<div id="root"></div>';
});

it('mounts main component and calls expected load helpers', async () => {
// Act
await act(async () => {
await doAppLoad();
});
await delay(100);

// Assert

expect(startPollingServiceAlerts).toBeCalledTimes(1);
expect(startPollingVersion).toBeCalledTimes(1);
expect(initializeAuthListeners).toBeCalledTimes(1);
expect(initializeAuthUser).toBeCalledTimes(1);
expect(initAuthTesting).toBeCalledTimes(1);
expect(initializeAuthMetrics).toBeCalledTimes(1);
expect(initializeSystemProperties).toBeCalledTimes(1);
expect(mountAjaxOverrideUtils).toBeCalledTimes(1);
expect(setupAjaxTestUtil).toBeCalledTimes(1);

expect(initAxeTools).toBeCalledTimes(0);

// main page component called for rendering
expect(Main).toBeCalledTimes(1);
});

it('initialized axe-tools when configured to do so', async () => {
// Arrange
asMockedFn(isAxeEnabled).mockReturnValue(true);

// Act
await act(async () => {
await doAppLoad();
});
await delay(100);

// Assert

expect(startPollingServiceAlerts).toBeCalledTimes(1);
expect(startPollingVersion).toBeCalledTimes(1);
expect(initializeAuthListeners).toBeCalledTimes(1);
expect(initializeAuthUser).toBeCalledTimes(1);
expect(initAuthTesting).toBeCalledTimes(1);
expect(initializeAuthMetrics).toBeCalledTimes(1);
expect(initializeSystemProperties).toBeCalledTimes(1);
expect(mountAjaxOverrideUtils).toBeCalledTimes(1);
expect(setupAjaxTestUtil).toBeCalledTimes(1);

// should now be called
expect(initAxeTools).toBeCalledTimes(1);

// main page component called for rendering
expect(Main).toBeCalledTimes(1);
});
});
58 changes: 58 additions & 0 deletions src/libs/startup/app-loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { LoDashStatic } from 'lodash';
import _ from 'lodash/fp';
import { createRoot } from 'react-dom/client';
import { h } from 'react-hyperscript-helpers';
import RModal from 'react-modal';
import { startPollingServiceAlerts } from 'src/alerts/service-alerts-polling';
import { startPollingVersion } from 'src/alerts/version-polling';
import { initializeAuthListeners, initializeAuthUser } from 'src/auth/app-load/init-auth';
import { initAuthTesting } from 'src/auth/app-load/init-auth-test';
import { initializeAuthMetrics } from 'src/auth/app-load/init-metrics';
import { initializeClientId } from 'src/auth/app-load/initializeClientId';
import { initializeSystemProperties } from 'src/auth/system-loader';
import { mountAjaxOverrideUtils } from 'src/libs/ajax/ajax-override-utils';
import { isAxeEnabled } from 'src/libs/config';
import { setupAjaxTestUtil } from 'src/libs/startup/ajax-test-root';
import Main from 'src/pages/Main';

declare global {
interface Window {
_: _.LoDashFp & LoDashStatic;
}
}

export const doAppLoad = async () => {
const rootElement = document.getElementById('root');

RModal.defaultStyles = { overlay: {}, content: {} };

// a shim for package build compatibility
window._ = _ as _.LoDashFp & LoDashStatic;

setupAjaxTestUtil();
mountAjaxOverrideUtils();

initializeAuthListeners();
initializeAuthMetrics();
initAuthTesting();

await initializeClientId();

const root = createRoot(rootElement!);
root.render(h(Main));

// react-notifications-component sets up its Store in the componentDidMount method
// of the ReactNotifications component. Use setTimeout to allow that to happen before
// doing anything that may show a notification.
setTimeout(() => {
void initializeSystemProperties();
void initializeAuthUser();
startPollingServiceAlerts();
startPollingVersion();
}, 0);

if (isAxeEnabled()) {
const { initAxeTools } = await import('src/libs/startup/axe-core');
void initAxeTools();
}
};
35 changes: 35 additions & 0 deletions src/libs/startup/axe-core.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import axe from '@axe-core/react';
import React from 'react';
import ReactDOM from 'react-dom';

import { initAxeTools } from './axe-core';

type AxeCoreExports = {
__esModule: true;
default: typeof import('@axe-core/react');
};
jest.mock(
'@axe-core/react',
(): AxeCoreExports => ({
__esModule: true,
default: jest.fn(),
})
);
describe('initAxeTools', () => {
it('initializes Axe Tools with desired settings', async () => {
// Act
await initAxeTools();

// Assert
expect(axe).toBeCalledTimes(1);
expect(axe).toBeCalledWith(React, ReactDOM, 1000, {
tags: ['wcag2a', 'wcag2aa'],
rules: [
{
id: 'color-contrast',
excludeHidden: true,
},
],
});
});
});
17 changes: 17 additions & 0 deletions src/libs/startup/axe-core.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import axe from '@axe-core/react';
import React from 'react';
import ReactDOM from 'react-dom';

export const initAxeTools = async () => {
const config = {
tags: ['wcag2a', 'wcag2aa'],
rules: [
{
id: 'color-contrast',
excludeHidden: true,
},
],
};

await axe(React, ReactDOM, 1000, config);
};
1 change: 0 additions & 1 deletion src/pages/ImportWorkflow/ImportWorkflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { importDockstoreWorkflow } from './importDockstoreWorkflow';
import { ImportWorkflow } from './ImportWorkflow';
import { useDockstoreWdl } from './useDockstoreWdl';

jest.mock('src/libs/ajax');
jest.mock('src/libs/ajax/leonardo/Apps');
jest.mock('src/libs/ajax/billing/Billing');
jest.mock('src/libs/ajax/workflows-app/Cbas');
Expand Down
2 changes: 0 additions & 2 deletions src/pages/workflows/workflow-details/WorkflowWdl.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { snapshotStore } from 'src/libs/state';
import { BaseWorkflowWdl } from 'src/pages/workflows/workflow-details/WorkflowWdl';
import { renderWithAppContexts } from 'src/testing/test-utils';

jest.mock('src/libs/ajax');

jest.mock('src/libs/notifications');

type NavExports = typeof import('src/libs/nav');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import React from 'react';
import { FindWorkflowModal } from 'src/pages/workspaces/workspace/modals/FindWorkflowModal';
import { renderWithAppContexts as render } from 'src/testing/test-utils';

jest.mock('src/libs/ajax');
jest.mock('src/libs/nav', () => ({
...jest.requireActual('src/libs/nav'),
getLink: jest.fn(() => '#workflows'),
Expand Down
1 change: 0 additions & 1 deletion src/profile/Profile.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { useUserProfile } from './useUserProfile';

// Workaround for import cycle.
jest.mock('src/auth/auth');
jest.mock('src/libs/ajax');

type SignOutExports = typeof import('src/auth/signout/sign-out');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { authStore, TermsOfServiceStatus } from 'src/libs/state';
import * as TosAlerts from 'src/registration/terms-of-service/terms-of-service-alerts';
import { renderWithAppContexts as render } from 'src/testing/test-utils';

jest.mock('src/libs/ajax');
jest.mock('src/libs/ajax/Metrics');

jest.mock('src/libs/nav', () => ({
Expand Down
Loading
Loading