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-204 Narrow Ajax Usage pt17 #5200

Merged
merged 2 commits into from
Dec 12, 2024
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
22 changes: 17 additions & 5 deletions src/groups/NewGroupModal.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { fireEvent, waitFor } from '@testing-library/react';
import { act, fireEvent, waitFor } from '@testing-library/react';
import { userEvent } from '@testing-library/user-event';
import React from 'react';
import { NewGroupModal } from 'src/groups/NewGroupModal';
Expand All @@ -20,9 +20,9 @@ jest.mock('src/libs/ajax/Groups');

describe('NewGroupModal', () => {
it('renders correctly', () => {
// Arrange
// Act
const { getByText } = render(<NewGroupModal onDismiss={jest.fn()} onSuccess={jest.fn()} existingGroups={[]} />);

// Assert
expect(getByText('Create Group')).toBeInTheDocument();
});
Expand All @@ -35,8 +35,10 @@ describe('NewGroupModal', () => {
);
const checkbox = getByLabelText('Allow anyone to request access');
expect(checkbox).toBeChecked();

// Act
await user.click(checkbox);

// Assert
expect(checkbox).not.toBeChecked();
});
Expand All @@ -46,10 +48,12 @@ describe('NewGroupModal', () => {
const { getByText, getByLabelText } = render(
<NewGroupModal onDismiss={jest.fn()} onSuccess={jest.fn()} existingGroups={[]} />
);

// Act
expect(getByText('Create Group')).toHaveAttribute('aria-disabled', 'true');
const nameInput = getByLabelText('Enter a unique name *');
fireEvent.change(nameInput, { target: { value: 'ValidName' } });

// Assert
await waitFor(() => expect(getByText('Create Group')).not.toBeDisabled());
});
Expand All @@ -59,12 +63,13 @@ describe('NewGroupModal', () => {
const { getByText, getByLabelText } = render(
<NewGroupModal onDismiss={jest.fn()} onSuccess={jest.fn()} existingGroups={[]} />
);

// Act
const nameInput = getByLabelText('Enter a unique name *');
fireEvent.change(nameInput, { target: { value: 'Invalid Name&' } });

// Assert
waitFor(() =>
await waitFor(() =>
expect(getByText('Group name can only contain letters, numbers, underscores, and dashes')).toBeInTheDocument()
);
});
Expand All @@ -75,11 +80,14 @@ describe('NewGroupModal', () => {
const { getByText, getByLabelText } = render(
<NewGroupModal onDismiss={jest.fn()} onSuccess={jest.fn()} existingGroups={[]} />
);

// Act
const nameInput = getByLabelText('Enter a unique name *');
fireEvent.change(nameInput, { target: { value: 'Valid Name' } });
await waitFor(() => expect(nameInput).toHaveValue('Valid Name'));
await act(async () => {
fireEvent.change(nameInput, { target: { value: 'Valid Name' } });
});
await user.clear(nameInput);

// Assert
expect(getByText("Group name can't be blank")).toBeInTheDocument();
});
Expand All @@ -90,9 +98,11 @@ describe('NewGroupModal', () => {
const { getByText, getByLabelText } = render(
<NewGroupModal onDismiss={jest.fn()} onSuccess={jest.fn()} existingGroups={[existingName]} />
);

// Act
const nameInput = getByLabelText('Enter a unique name *');
fireEvent.change(nameInput, { target: { value: existingName } });

// Assert
await waitFor(() => expect(getByText('Group name already exists')).toBeInTheDocument());
});
Expand Down Expand Up @@ -120,8 +130,10 @@ describe('NewGroupModal', () => {
await waitFor(() => expect(nameInput).toHaveValue('ValidName'));
const submitButton = getByText('Create Group');
expect(submitButton).toBeEnabled();

// Act
await user.click(submitButton);

// Assert
expect(mockCreateFn).toHaveBeenCalled();
expect(mockSetPolicyFn).toHaveBeenCalledWith('admin-notifier', true);
Expand Down
93 changes: 37 additions & 56 deletions src/workspace-data/ImportStatus.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { DeepPartial } from '@terra-ui-packages/core-utils';
import { asMockedFn } from '@terra-ui-packages/test-utils';
import { asMockedFn, MockedFn, partial } from '@terra-ui-packages/test-utils';
import { act } from '@testing-library/react';
import { h } from 'react-hyperscript-helpers';
import { Ajax } from 'src/libs/ajax';
import { WDSJob, WorkspaceData, WorkspaceDataAjaxContract } from 'src/libs/ajax/WorkspaceDataService';
import { WorkspaceContract, Workspaces, WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces';
import { notify } from 'src/libs/notifications';
import { asyncImportJobStore } from 'src/libs/state';
import { renderWithAppContexts as render } from 'src/testing/test-utils';

import ImportStatus from './ImportStatus';

jest.mock('src/libs/ajax');
type AjaxExports = typeof import('src/libs/ajax');
type AjaxContract = ReturnType<AjaxExports['Ajax']>;
jest.mock('src/libs/ajax/WorkspaceDataService');
jest.mock('src/libs/ajax/workspaces/Workspaces');

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

Expand All @@ -30,15 +29,13 @@ describe('ImportStatus', () => {
{ targetWorkspace: { namespace: 'test-workspaces', name: 'google-workspace' }, jobId: 'workspace-job-1' },
]);

const getImportJobStatus = jest.fn().mockResolvedValue({ status: 'Pending' });
const mockAjax: DeepPartial<AjaxContract> = {
Workspaces: {
workspace: (_namespace, _name) => ({
getImportJobStatus,
}),
},
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
const getImportJobStatus: MockedFn<WorkspaceContract['getImportJobStatus']> = jest.fn();
getImportJobStatus.mockResolvedValue({ status: 'Pending' });
asMockedFn(Workspaces).mockReturnValue(
partial<WorkspacesAjaxContract>({
workspace: (_namespace, _name) => partial<WorkspaceContract>({ getImportJobStatus }),
})
);

// Act
render(h(ImportStatus, {}));
Expand All @@ -62,15 +59,13 @@ describe('ImportStatus', () => {
{ targetWorkspace: { namespace: 'test-workspaces', name: 'google-workspace' }, jobId: 'workspace-job-1' },
]);

const getImportJobStatus = jest.fn().mockResolvedValue({ status: 'Error', message: 'There has been an error.' });
const mockAjax: DeepPartial<AjaxContract> = {
Workspaces: {
workspace: (_namespace, _name) => ({
getImportJobStatus,
}),
},
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
const getImportJobStatus: MockedFn<WorkspaceContract['getImportJobStatus']> = jest.fn();
getImportJobStatus.mockResolvedValue({ status: 'Error', message: 'There has been an error.' });
asMockedFn(Workspaces).mockReturnValue(
partial<WorkspacesAjaxContract>({
workspace: (_namespace, _name) => partial<WorkspaceContract>({ getImportJobStatus }),
})
);

// Act
render(h(ImportStatus, {}));
Expand All @@ -89,15 +84,13 @@ describe('ImportStatus', () => {
{ targetWorkspace: { namespace: 'test-workspaces', name: 'google-workspace' }, jobId: 'workspace-job-1' },
]);

const getImportJobStatus = jest.fn().mockResolvedValue({ status: 'Done' });
const mockAjax: DeepPartial<AjaxContract> = {
Workspaces: {
workspace: (_namespace, _name) => ({
getImportJobStatus,
}),
},
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
const getImportJobStatus: MockedFn<WorkspaceContract['getImportJobStatus']> = jest.fn();
getImportJobStatus.mockResolvedValue({ status: 'Done' });
asMockedFn(Workspaces).mockReturnValue(
partial<WorkspacesAjaxContract>({
workspace: (_namespace, _name) => partial<WorkspaceContract>({ getImportJobStatus }),
})
);

// Act
render(h(ImportStatus, {}));
Expand Down Expand Up @@ -132,13 +125,9 @@ describe('ImportStatus', () => {
},
]);

const getJobStatus = jest.fn().mockResolvedValue({ status: 'QUEUED' });
const mockAjax: DeepPartial<AjaxContract> = {
WorkspaceData: {
getJobStatus,
},
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
const getJobStatus: MockedFn<WorkspaceDataAjaxContract['getJobStatus']> = jest.fn();
getJobStatus.mockResolvedValue(partial<WDSJob>({ status: 'QUEUED' }));
asMockedFn(WorkspaceData).mockReturnValue(partial<WorkspaceDataAjaxContract>({ getJobStatus }));

// Act
render(h(ImportStatus, {}));
Expand Down Expand Up @@ -166,15 +155,11 @@ describe('ImportStatus', () => {
},
]);

const getJobStatus = jest
.fn()
.mockResolvedValue({ status: 'ERROR', errorMessage: 'Import failed for some reason.' });
const mockAjax: DeepPartial<AjaxContract> = {
WorkspaceData: {
getJobStatus,
},
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
const getJobStatus: MockedFn<WorkspaceDataAjaxContract['getJobStatus']> = jest.fn();
getJobStatus.mockResolvedValue(
partial<WDSJob>({ status: 'ERROR', errorMessage: 'Import failed for some reason.' })
);
asMockedFn(WorkspaceData).mockReturnValue(partial<WorkspaceDataAjaxContract>({ getJobStatus }));

// Act
render(h(ImportStatus, {}));
Expand All @@ -196,13 +181,9 @@ describe('ImportStatus', () => {
},
]);

const getJobStatus = jest.fn().mockResolvedValue({ status: 'SUCCEEDED' });
const mockAjax: DeepPartial<AjaxContract> = {
WorkspaceData: {
getJobStatus,
},
};
asMockedFn(Ajax).mockImplementation(() => mockAjax as AjaxContract);
const getJobStatus: MockedFn<WorkspaceDataAjaxContract['getJobStatus']> = jest.fn();
getJobStatus.mockResolvedValue(partial<WDSJob>({ status: 'SUCCEEDED' }));
asMockedFn(WorkspaceData).mockReturnValue(partial<WorkspaceDataAjaxContract>({ getJobStatus }));

// Act
render(h(ImportStatus, {}));
Expand Down
7 changes: 4 additions & 3 deletions src/workspace-data/ImportStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import _ from 'lodash/fp';
import { Fragment, ReactNode } from 'react';
import { h, p } from 'react-hyperscript-helpers';
import { Link } from 'src/components/common';
import { Ajax } from 'src/libs/ajax';
import { WorkspaceData } from 'src/libs/ajax/WorkspaceDataService';
import { Workspaces } from 'src/libs/ajax/workspaces/Workspaces';
import { withErrorReporting } from 'src/libs/error';
import { clearNotification, notify } from 'src/libs/notifications';
import { useCancellation, usePollingEffect, useStore } from 'src/libs/react-utils';
Expand Down Expand Up @@ -56,9 +57,9 @@ const ImportStatusItem = (props: ImportStatusItemProps): ReactNode => {
const fetchImportStatus = async () => {
try {
if (wdsProxyUrl) {
return await Ajax(signal).WorkspaceData.getJobStatus(wdsProxyUrl, jobId);
return await WorkspaceData(signal).getJobStatus(wdsProxyUrl, jobId);
}
return await Ajax(signal).Workspaces.workspace(namespace, name).getImportJobStatus(jobId);
return await Workspaces(signal).workspace(namespace, name).getImportJobStatus(jobId);
} catch (error: unknown) {
// Ignore 404; We're probably asking for status before the status endpoint knows about the job
if (error instanceof Response && error.status === 404) {
Expand Down
14 changes: 7 additions & 7 deletions src/workspace-data/WorkspaceAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { DelayedSearchInput, TextInput } from 'src/components/input';
import { MenuButton } from 'src/components/MenuButton';
import { MenuDivider, MenuTrigger } from 'src/components/PopupTrigger';
import { FlexTable, HeaderCell } from 'src/components/table';
import { Ajax } from 'src/libs/ajax';
import { Workspaces } from 'src/libs/ajax/workspaces/Workspaces';
import colors from 'src/libs/colors';
import { withErrorReporting } from 'src/libs/error';
import * as Style from 'src/libs/style';
Expand Down Expand Up @@ -113,8 +113,8 @@ export const WorkspaceAttributes = ({
...(!editDescription ? [editDescriptionKey] : []),
];

await Ajax().Workspaces.workspace(namespace, name).shallowMergeNewAttributes(attributesToMerge);
await Ajax().Workspaces.workspace(namespace, name).deleteAttributes(attributesToDelete);
await Workspaces().workspace(namespace, name).shallowMergeNewAttributes(attributesToMerge);
await Workspaces().workspace(namespace, name).deleteAttributes(attributesToDelete);

await refreshAttributes();

Expand All @@ -126,15 +126,15 @@ export const WorkspaceAttributes = ({
withErrorReporting('Error uploading file'),
Utils.withBusyState(setBusy)
)(async ([file]) => {
await Ajax().Workspaces.workspace(namespace, name).importAttributes(file);
await Workspaces().workspace(namespace, name).importAttributes(file);
await refreshAttributes();
});

const download = _.flow(
withErrorReporting('Error downloading attributes'),
Utils.withBusyState(setBusy)
)(async () => {
const blob = await Ajax().Workspaces.workspace(namespace, name).exportAttributes();
const blob = await Workspaces().workspace(namespace, name).exportAttributes();
FileSaver.saveAs(blob, `${name}-workspace-attributes.tsv`);
});

Expand Down Expand Up @@ -443,8 +443,8 @@ export const WorkspaceAttributes = ({
withErrorReporting('Error deleting workspace variables')
)(async () => {
setDeleting(false);
await Ajax()
.Workspaces.workspace(namespace, name)
await Workspaces()
.workspace(namespace, name)
.deleteAttributes(
Object.entries(selection)
.filter(([_, selected]) => selected)
Expand Down
27 changes: 10 additions & 17 deletions src/workspace-data/WorkspaceAttributes.test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { DeepPartial, delay } from '@terra-ui-packages/core-utils';
import { asMockedFn } from '@terra-ui-packages/test-utils';
import { delay } from '@terra-ui-packages/core-utils';
import { asMockedFn, MockedFn, partial } from '@terra-ui-packages/test-utils';
import { act, fireEvent, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Ajax } from 'src/libs/ajax';
import { WorkspaceContract, Workspaces, WorkspacesAjaxContract } from 'src/libs/ajax/workspaces/Workspaces';
import { renderWithAppContexts as render } from 'src/testing/test-utils';
import { defaultGoogleWorkspace } from 'src/testing/workspace-fixtures';
import { WorkspaceWrapper } from 'src/workspaces/utils';
Expand Down Expand Up @@ -40,16 +40,7 @@ jest.mock('react-virtualized', (): ReactVirtualizedExports => {
};
});

type AjaxExports = typeof import('src/libs/ajax');
jest.mock(
'src/libs/ajax',
(): AjaxExports => ({
...jest.requireActual<AjaxExports>('src/libs/ajax'),
Ajax: jest.fn(),
})
);

type AjaxContract = ReturnType<typeof Ajax>;
jest.mock('src/libs/ajax/workspaces/Workspaces');

describe('WorkspaceAttributes', () => {
interface SetupArgs {
Expand Down Expand Up @@ -165,11 +156,13 @@ describe('WorkspaceAttributes', () => {

it('allows selecting and deleting attributes', async () => {
// Arrange/Act
const deleteAttributes = jest.fn().mockResolvedValue(undefined);
const workspace = jest.fn(() => ({ deleteAttributes }));
const deleteAttributes: MockedFn<WorkspaceContract['deleteAttributes']> = jest.fn();
deleteAttributes.mockResolvedValue(undefined);

asMockedFn(Ajax).mockImplementation(
() => ({ Workspaces: { workspace } } as DeepPartial<AjaxContract> as AjaxContract)
asMockedFn(Workspaces).mockReturnValue(
partial<WorkspacesAjaxContract>({
workspace: () => partial<WorkspaceContract>({ deleteAttributes }),
})
);

setup({
Expand Down
Loading
Loading