Skip to content

Commit

Permalink
Merge branch 'dev' into tl_ID-1113_split_out_logout_code
Browse files Browse the repository at this point in the history
  • Loading branch information
tlangs authored Mar 20, 2024
2 parents 9d34969 + 464fcac commit 39c1ba5
Show file tree
Hide file tree
Showing 27 changed files with 1,376 additions and 476 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
/src/pages/LandingPage* @DataBiosphere/terra-cobranding
/src/pages/billing/ @DataBiosphere/broadworkspaces-terra-ui
/src/pages/workspaces/* @DataBiosphere/broadworkspaces-terra-ui
/src/profile/notification-settings/* @DataBiosphere/broadworkspaces-terra-ui
/src/workflows-app/ @DataBiosphere/broad-workflow-management @DataBiosphere/broad-workflow-execution
/src/workspace-data/ @DataBiosphere/analysisjourneys
/src/workspaces/ @DataBiosphere/broadworkspaces-terra-ui
/integration-tests/tests/billing-projects.js @DataBiosphere/broadworkspaces-terra-ui
/integration-tests/tests/delete-orphaned-workspaces.js @DataBiosphere/broadworkspaces-terra-ui
/integration-tests/tests/workspace-dashboard.js @DataBiosphere/broadworkspaces-terra-ui
/integration-tests/tests/run-analysis.js @DataBiosphere/broad-interactive-analysis
/integration-tests/tests/run-analysis-azure.js @DataBiosphere/broad-interactive-analysis
/integration-tests/tests/run-rstudio.js @DataBiosphere/broad-interactive-analysis
Expand Down
43 changes: 41 additions & 2 deletions src/dataset-builder/ConceptSelector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ describe('ConceptSelector', () => {
const datasetId = '0';
// Using 101 so the ID doesn't match the count.
const rootConcept = { ...dummyGetConceptForId(100), children: [dummyGetConceptForId(101)] };
const initialCart: SnapshotBuilderConcept[] = [];
const renderSelector = () => {
const renderSelector = (initialCart: SnapshotBuilderConcept[] = []) => {
render(
h(ConceptSelector, {
actionText,
Expand All @@ -40,6 +39,8 @@ describe('ConceptSelector', () => {
};

const firstChild = rootConcept.children[0];
const secondChild = rootConcept.children[1];

it('renders the concept selector', () => {
// Arrange
renderSelector();
Expand All @@ -52,6 +53,22 @@ describe('ConceptSelector', () => {
expect(screen.queryByText(actionText)).toBeFalsy();
});

it('renders the concept selector with a non-empty initial cart', () => {
// Arrange
renderSelector([firstChild]);
// Assert
expect(screen.queryByText(actionText)).toBeTruthy();
expect(screen.queryByText('1 concept', { exact: false })).toBeTruthy();
});

it('renders the concept selector with a multiple concepts in initial cart', () => {
// Arrange
renderSelector([firstChild, secondChild]);
// Assert
expect(screen.queryByText(actionText)).toBeTruthy();
expect(screen.queryByText('2 concepts', { exact: false })).toBeTruthy();
});

it('supports add to cart', async () => {
// Arrange
renderSelector();
Expand All @@ -63,6 +80,17 @@ describe('ConceptSelector', () => {
expect(screen.queryByText('1 concept', { exact: false })).toBeTruthy();
});

it('supports add to cart with existing cart items', async () => {
// Arrange
renderSelector([secondChild]);
// Act
const user = userEvent.setup();
await user.click(screen.getByLabelText(`add ${firstChild.id}`));
// Assert
expect(screen.queryByText(actionText)).toBeTruthy();
expect(screen.queryByText('2 concept', { exact: false })).toBeTruthy();
});

it('supports remove from cart', async () => {
// Arrange
renderSelector();
Expand All @@ -75,6 +103,17 @@ describe('ConceptSelector', () => {
expect(screen.queryByText('1 concept', { exact: false })).toBeFalsy();
});

it('supports remove from cart, when previously in cart', async () => {
// Arrange
renderSelector([firstChild]);
// Act
const user = userEvent.setup();
await user.click(screen.getByLabelText(`remove ${firstChild.id}`));
// Assert
expect(screen.queryByText(actionText)).toBeFalsy();
expect(screen.queryByText('1 concept', { exact: false })).toBeFalsy();
});

it('calls commit on action', async () => {
// Arrange
renderSelector();
Expand Down
13 changes: 9 additions & 4 deletions src/dataset-builder/ConceptSelector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,20 @@ export const ConceptSelector = (props: ConceptSelectorProps) => {
width: 710,
render: (concept) => {
const [label, iconName]: [string, IconId] = (() => {
if (_.contains(concept, cart)) {
if (_.filter(_.isEqual(concept), cart).length > 0) {
return ['remove', 'minus-circle-red'];
}
return ['add', 'plus-circle-filled'];
})();
return h(Fragment, [
h(Link, { 'aria-label': `${label} ${concept.id}`, onClick: () => setCart(_.xor(cart, [concept])) }, [
icon(iconName, { size: 16 }),
]),
h(
Link,
{
'aria-label': `${label} ${concept.id}`,
onClick: () => setCart(_.xorWith(_.isEqual, cart, [concept])),
},
[icon(iconName, { size: 16 })]
),
div({ style: { marginLeft: 5 } }, [
openedConcept?.id === concept.id ? div({ style: { fontWeight: 600 } }, [concept.name]) : concept.name,
]),
Expand Down
27 changes: 19 additions & 8 deletions src/dataset-builder/ConceptSetCreator.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { h } from 'react-hyperscript-helpers';
import { SnapshotBuilderConcept as Concept } from 'src/libs/ajax/DataRepo';
import { renderWithAppContexts as render } from 'src/testing/test-utils';

import { ConceptSetCreator, toConceptSet } from './ConceptSetCreator';
import { ConceptSetCreator, toDomainConceptSet } from './ConceptSetCreator';
import { homepageState } from './dataset-builder-types';
import { dummyDatasetModel } from './TestConstants';

Expand All @@ -12,44 +13,54 @@ jest.mock('src/libs/ajax/GoogleStorage');
describe('ConceptSetCreator', () => {
const dataset = dummyDatasetModel();

const renderConceptSetCreator = () => {
const renderConceptSetCreator = (cart: Concept[]) => {
const conceptSetUpdater = jest.fn();
const onStateChange = jest.fn();
render(
h(ConceptSetCreator, {
dataset,
onStateChange,
conceptSetUpdater,
cart,
})
);
return { conceptSetUpdater, onStateChange };
};

const rootConcept = dataset!.snapshotBuilderSettings!.domainOptions[0].root;

it('renders the domain criteria selector', async () => {
it('renders the concept set selector', async () => {
// Arrange
renderConceptSetCreator();
renderConceptSetCreator([]);
// Assert
expect(await screen.findByText(rootConcept.name)).toBeTruthy();
});

it('renders the concept set selector with an item in the cart', async () => {
// Arrange
renderConceptSetCreator([rootConcept]);
// Assert
expect(screen.queryByText('1 concept', { exact: false })).toBeTruthy();
});

it('updates the builder concept sets on save', async () => {
// Arrange
const { conceptSetUpdater, onStateChange } = renderConceptSetCreator();
const { conceptSetUpdater, onStateChange } = renderConceptSetCreator([]);
// rootConcept will be modified in ConceptSetCreator, hasChildren: true -> false
const rootConceptNoChildren = { ...rootConcept, hasChildren: false };
// Act
const user = userEvent.setup();
// Click the add button for the root concept.
await user.click(screen.getByLabelText(`add ${rootConcept.id}`));
await user.click(screen.getByLabelText(`add ${rootConceptNoChildren.id}`));
await user.click(screen.getByText('Add to concept sets'));
// Assert
expect(onStateChange).toHaveBeenCalledWith(homepageState.new());
expect(conceptSetUpdater.mock.calls[0][0]([])).toEqual([toConceptSet(rootConcept)]);
expect(conceptSetUpdater.mock.calls[0][0]([])).toEqual([toDomainConceptSet(rootConceptNoChildren)]);
});

it('returns to the home page on cancel', async () => {
// Arrange
const { conceptSetUpdater, onStateChange } = renderConceptSetCreator();
const { conceptSetUpdater, onStateChange } = renderConceptSetCreator([]);
// Act
const user = userEvent.setup();
await user.click(screen.getByLabelText('cancel'));
Expand Down
29 changes: 21 additions & 8 deletions src/dataset-builder/ConceptSetCreator.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import _ from 'lodash/fp';
import { h } from 'react-hyperscript-helpers';
import { ConceptSet } from 'src/dataset-builder/DatasetBuilderUtils';
import { DatasetModel, SnapshotBuilderConcept as Concept } from 'src/libs/ajax/DataRepo';
import { DatasetModel, DomainConceptSet, SnapshotBuilderConcept as Concept } from 'src/libs/ajax/DataRepo';

import { ConceptSelector } from './ConceptSelector';
import { homepageState, Updater } from './dataset-builder-types';
Expand All @@ -10,18 +9,27 @@ import { OnStateChangeHandler } from './DatasetBuilder';
export type ConceptSetCreatorProps = {
readonly onStateChange: OnStateChangeHandler;
readonly dataset: DatasetModel;
readonly conceptSetUpdater: Updater<ConceptSet[]>;
readonly conceptSetUpdater: Updater<DomainConceptSet[]>;
readonly cart: Concept[];
};

export const toConceptSet = (concept: Concept): ConceptSet => {
// featureValueGroupName represents a domain name
// this works because the only concepts passed in are also domains
// such as Condition
export const toDomainConceptSet = (concept: Concept): DomainConceptSet => {
return {
name: concept.name,
concept,
featureValueGroupName: concept.name,
};
};

export const toConcept = (conceptSet: DomainConceptSet): Concept => {
return conceptSet.concept;
};

export const ConceptSetCreator = (props: ConceptSetCreatorProps) => {
const { onStateChange, dataset, conceptSetUpdater } = props;
const { onStateChange, dataset, conceptSetUpdater, cart } = props;
const { snapshotBuilderSettings, id } = dataset;
// create a root for all domainOptions
const domainOptionRoot: Concept = {
Expand All @@ -32,12 +40,17 @@ export const ConceptSetCreator = (props: ConceptSetCreatorProps) => {
children: _.map(_.get('root'), snapshotBuilderSettings?.domainOptions),
};
return h(ConceptSelector, {
rootConcept: domainOptionRoot,
initialCart: [],
// Concept selection currently only supports top level domains, so nodes should not be expandable
rootConcept: {
...domainOptionRoot,
children: _.map((child) => ({ ...child, hasChildren: false }), domainOptionRoot.children),
},
initialCart: cart,
title: 'Add concept',
onCancel: () => onStateChange(homepageState.new()),
onCommit: (selected: Concept[]) => {
conceptSetUpdater((conceptSets) => _.flow(_.map(toConceptSet), _.union(conceptSets))(selected));
// commit ignores the current concept set selection and overwrites it with the cart
conceptSetUpdater(() => _.map(toDomainConceptSet, selected));
onStateChange(homepageState.new());
},
actionText: 'Add to concept sets',
Expand Down
30 changes: 17 additions & 13 deletions src/dataset-builder/DatasetBuilder.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { fireEvent, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import _ from 'lodash/fp';
import { h } from 'react-hyperscript-helpers';
import { Cohort, ConceptSet } from 'src/dataset-builder/DatasetBuilderUtils';
import { DataRepo, DataRepoContract, DatasetModel } from 'src/libs/ajax/DataRepo';
import { Cohort } from 'src/dataset-builder/DatasetBuilderUtils';
import { ConceptSet, DataRepo, DataRepoContract, DatasetModel, DomainConceptSet } from 'src/libs/ajax/DataRepo';
import * as Nav from 'src/libs/nav';
import { asMockedFn, renderWithAppContexts as render } from 'src/testing/test-utils';

Expand Down Expand Up @@ -34,15 +34,17 @@ jest.mock('src/libs/ajax/DataRepo', (): DataRepoExports => {
};
});

const concept = { id: 100, name: 'concept', count: 10, hasChildren: false, children: [] };

describe('DatasetBuilder', () => {
const dummyDatasetDetailsWithId = dummyDatasetModel();
type DatasetBuilderContentsPropsOverrides = {
onStateChange?: OnStateChangeHandler;
updateCohorts?: Updater<Cohort[]>;
updateConceptSets?: Updater<ConceptSet[]>;
updateConceptSets?: Updater<DomainConceptSet[]>;
dataset?: DatasetModel;
cohorts?: Cohort[];
conceptSets?: ConceptSet[];
conceptSets?: DomainConceptSet[];
};
const showDatasetBuilderContents = (overrides?: DatasetBuilderContentsPropsOverrides) => {
render(
Expand Down Expand Up @@ -78,7 +80,7 @@ describe('DatasetBuilder', () => {
const initializeValidDatasetRequest = async (user) => {
showDatasetBuilderContents({
cohorts: [newCohort('cohort 1')],
conceptSets: [{ name: 'concept set 1', featureValueGroupName: 'Condition' }],
conceptSets: [{ name: 'concept set 1', concept, featureValueGroupName: 'Condition' }],
});
await user.click(screen.getByLabelText('cohort 1'));
await user.click(screen.getByLabelText('concept set 1'));
Expand All @@ -104,8 +106,8 @@ describe('DatasetBuilder', () => {
render(
h(ConceptSetSelector, {
conceptSets: [
{ name: 'concept set 1', featureValueGroupName: 'a' },
{ name: 'concept set 2', featureValueGroupName: 'b' },
{ name: 'concept set 1', concept, featureValueGroupName: 'a' },
{ name: 'concept set 2', concept, featureValueGroupName: 'b' },
],
prepackagedConceptSets: dummyDatasetDetailsWithId!.snapshotBuilderSettings!.datasetConceptSets,
selectedConceptSets: [],
Expand Down Expand Up @@ -198,8 +200,8 @@ describe('DatasetBuilder', () => {
showDatasetBuilderContents({
cohorts: [newCohort('cohort 1'), newCohort('cohort 2')],
conceptSets: [
{ name: 'concept set 1', featureValueGroupName: 'Condition' },
{ name: 'concept set 2', featureValueGroupName: 'Procedure' },
{ name: 'concept set 1', concept, featureValueGroupName: 'Condition' },
{ name: 'concept set 2', concept, featureValueGroupName: 'Procedure' },
],
});
// Act
Expand Down Expand Up @@ -232,7 +234,9 @@ describe('DatasetBuilder', () => {
it('places selectable values defaulted to selected when concept set is selected', async () => {
// Arrange
const user = userEvent.setup();
showDatasetBuilderContents({ conceptSets: [{ name: 'concept set 1', featureValueGroupName: 'Condition' }] });
showDatasetBuilderContents({
conceptSets: [{ name: 'concept set 1', concept, featureValueGroupName: 'Condition' }],
});
// Act
await user.click(screen.getByLabelText('concept set 1'));
// Assert
Expand Down Expand Up @@ -308,7 +312,7 @@ describe('DatasetBuilder', () => {
// Act
await user.click(await screen.findByLabelText('Create new concept set'));
// Assert
expect(onStateChange).toHaveBeenCalledWith({ mode: 'concept-set-creator' });
expect(onStateChange).toHaveBeenCalledWith({ mode: 'concept-set-creator', cart: [] });
});

it('enables editing cohorts', async () => {
Expand Down Expand Up @@ -345,8 +349,8 @@ describe('DatasetBuilder', () => {
const user = userEvent.setup();
const updateConceptSets = jest.fn();
const conceptSets = [
{ name: 'concept set 1', featureValueGroupName: 'Condition' },
{ name: 'concept set 2', featureValueGroupName: 'Procedure' },
{ name: 'concept set 1', concept, featureValueGroupName: 'Condition' },
{ name: 'concept set 2', concept, featureValueGroupName: 'Procedure' },
];
showDatasetBuilderContents({ updateConceptSets, conceptSets });
// Act
Expand Down
Loading

0 comments on commit 39c1ba5

Please sign in to comment.