Skip to content

Commit

Permalink
Update tests
Browse files Browse the repository at this point in the history
Updated test coverage for groups selection to accomodate the new
interface.  Added tests checking that rows are filtered as expected.
  • Loading branch information
brianlove committed Oct 31, 2023
1 parent d1692e3 commit 5556c6f
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 89 deletions.
101 changes: 39 additions & 62 deletions web/gui-v2/src/components/ListView.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { userEventSetup } from '../util/testing';
import ListView from './ListView';
import { exportsForTestingOnly } from './ListViewTable';

const { filterRow } = exportsForTestingOnly;
const { extractCurrentFilters, filterRow } = exportsForTestingOnly;

const INITIAL_COLUMNS = ['Company', 'Country', 'AI publications', 'AI patents', 'Tech Tier 1 jobs'];
const REMOVED_COLUMN = 'AI publications';
Expand All @@ -34,6 +34,20 @@ describe("ListView", () => {
expect(screen.getByText('Viewing 1760 companies')).toBeVisible();
}, 20000);


it("selects a group of companies", async () => {
const { user } = userEventSetup(
<ListView />
);

const companyHeader = screen.getByRole('columnheader', { name: /company/i });
await user.click(getByRole(companyHeader, 'combobox'));
const menu = screen.getByRole('listbox');
await user.click(getByText(menu, 'S&P 500'));
expect(screen.getByText('Viewing 499 of 1760 companies')).toBeVisible();
});


describe("add/remove columns dialog", () => {
it("opens the dialog and changes columns", async () => {
const { user } = userEventSetup(
Expand Down Expand Up @@ -67,71 +81,34 @@ describe("ListView", () => {
}, 90000);
});

describe.skip('groups', () => {
it('switches to custom group mode', async () => {
const { user } = userEventSetup(
<ListView />
);

const groupSelectorWrapper = screen.getByTestId('group-selector');
await user.click(getByRole(groupSelectorWrapper, 'button'));

// Verify that the groups we expect are present
expect(screen.getByRole('option', { name: 'All companies' })).toBeVisible();
expect(screen.getByRole('option', { name: 'S&P 500' })).toBeVisible();
expect(screen.getByRole('option', { name: 'Custom' })).toBeVisible();
await user.click(screen.getByRole('option', { name: 'Custom' }));

// We don't have any companies in our group yet, so no results should be shown
expect(screen.getByText(/no companies selected/i)).toBeVisible();

// Open the group editor dialog
await user.click(screen.getByRole('button', { name: /edit custom group/i }));
expect(screen.getByRole('heading', { name: 'Modify custom company group' })).toBeVisible();
const dialog = screen.getByRole('dialog');
const companyInput = getByRole(dialog, 'combobox');
await user.click(companyInput);
await user.type(companyInput, 'Microsoft');
await user.click(getByRole(dialog, 'option', { name: 'Microsoft' }));
await user.click(getByRole(dialog, 'button', { name: 'Apply' }));
await waitForElementToBeRemoved(dialog);

// Confirm that our group selection is present in the table
const table = screen.getByRole('table');
expect(getByRole(table, 'row', { name: /Microsoft/ })).toBeVisible();

// Verify that a pre-existing group displays correctly
await user.click(getByRole(groupSelectorWrapper, 'button', { name: 'Custom' }));
expect(screen.getByRole('option', { name: 'S&P 500' })).toBeVisible();
await user.click(screen.getByRole('option', { name: 'S&P 500' }));
expect(getByRole(table, 'row', { name: /Microsoft/ })).toBeVisible();
expect(getByRole(table, 'row', { name: /IBM/ })).toBeVisible();
expect(getByRole(table, 'row', { name: /Google/ })).toBeVisible();
expect(getByRole(table, 'row', { name: /Apple/ })).toBeVisible();
expect(getByRole(table, 'row', { name: /3M/ })).toBeVisible();
}, 60000);
});


describe("helper functions", () => {
it("filtering rows works as expected", () => {
const FILTERS_SP500 = {
name: [ "GROUP:sandp_500" ],
country: [],
continent: [],
stage: [],
const FILTERS_RAW = {
name: {
get: [ "GROUP:sp500", "Sierra Nevada Corp" ],
},
};
const DJI = { cset_id: 78, name: "DJI", in_fortune_global_500: false, in_sandp_500: false };
const HUAWEI = { cset_id: 112, name: "Huawei", in_fortune_global_500: false, in_sandp_500: true };
const MICROSOFT = { cset_id: 163, name: "Microsoft", in_fortune_global_500: true, in_sandp_500: true };
const SAMSUNG = { cset_id: 671, name: "Samsung", in_fortune_global_500: false, in_sandp_500: true };
const THALES = { cset_id: 2794, name: "Thales SA", in_fortune_global_500: false, in_sandp_500: false };

expect(filterRow(DJI, FILTERS_SP500)).toEqual(false);
expect(filterRow(HUAWEI, FILTERS_SP500)).toEqual(true);
expect(filterRow(MICROSOFT, FILTERS_SP500)).toEqual(true);
expect(filterRow(SAMSUNG, FILTERS_SP500)).toEqual(true);
expect(filterRow(THALES, FILTERS_SP500)).toEqual(false);

const FILTERS_TRANSFORMED = extractCurrentFilters(FILTERS_RAW);

expect(FILTERS_TRANSFORMED).toEqual({
_groups: [ "sp500" ],
_companies: [ "Sierra Nevada Corp" ],
name: [ "GROUP:sp500", "Sierra Nevada Corp" ],
});

const MICROSOFT = { cset_id: 163, name: "Microsoft", groups: { sp500: true, global500: true } };
const SAMSUNG = { cset_id: 671, name: "Samsung", groups: { sp500: true, global500: false } };
const THALES = { cset_id: 2794, name: "Thales SA", groups: { sp500: false, global500: false } };
const QUALCOMM = { cset_id: 209, name: "Qualcomm", groups: { sp500: false, global500: true } };
const SIERRA_NEVADA = { cset_id: 2753, name: "Sierra Nevada Corp", groups: { sp500: false, global500: false } };

expect(filterRow(MICROSOFT, FILTERS_TRANSFORMED)).toEqual(true);
expect(filterRow(SAMSUNG, FILTERS_TRANSFORMED)).toEqual(true);
expect(filterRow(THALES, FILTERS_TRANSFORMED)).toEqual(false);
expect(filterRow(QUALCOMM, FILTERS_TRANSFORMED)).toEqual(false);
expect(filterRow(SIERRA_NEVADA, FILTERS_TRANSFORMED)).toEqual(true);
});
});
});
95 changes: 68 additions & 27 deletions web/gui-v2/src/components/ListViewTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,33 @@ const styles = {
`,
};

/**
* @typedef {string} ColumnKey The `key` property for a defined column
* @typedef {Array<string>} FilterDropdownValue The selected element(s) from a dropdown
* @typedef {[number, number]} FilterSliderValue The upper and lower bounds of a slider-based filter.
*/

/**
* A read-only version of the currently-applied filters, in a format that is
* easier to access.
* @typedef {{
* _companies: FilterDropdownValue | FilterSliderValue,
* _groups: FilterDropdownValue | FilterSliderValue,
* [key: ColumnKey]: FilterDropdownValue | FilterSliderValue,
* }} CurrentFiltersObject
* @readonly
*/

/**
* The current, definitive source for filter values, including access to the
* setters for updating filters.
* @typedef {{
* [key: ColumnKey]: {
* get get(): FilterDropdownValue | FilterSliderValue;
* set: (newVal: FilterDropdownValue | FilterSliderValue) => void;
* }
* }} FilterStateObject
*/

const GROUPS_OPTIONS = Object.entries(overallData.groups).map(([k, v]) => ({ text: v, val: `GROUP:${k}` }));

Expand Down Expand Up @@ -232,7 +259,47 @@ const filterRow = (row, filters) => {
};


/**
* Extract the current state of the filters and present them in a format that is
* easier to understand.
*
* @param {FilterStateObject} filters A `filters` object, as created by `useMultiState` and `useQueryParamString`.
* @returns {CurrentFiltersObject} A read-only version of the current state of the filters
* @readonly
*/
const extractCurrentFilters = (filters) => {
return Object.freeze(
Object.fromEntries(
Object.entries(filters)
.flatMap( ([key, { get }]) => {
if ( key === 'name' ) {
const groups = [];
const nonGroups = [];
get.forEach((entry) => {
if ( entry.startsWith('GROUP:') ) {
groups.push(entry.slice(6));
} else {
nonGroups.push(entry);
}
});
return [
[key, get],
['_groups', groups],
['_companies', nonGroups],
];
} else {
return [
[key, get],
];
}
})
)
);
};


export const exportsForTestingOnly = {
extractCurrentFilters,
filterRow,
};

Expand Down Expand Up @@ -284,33 +351,7 @@ const ListViewTable = ({

// Read-only object of the currently-set values of the filters
const currentFilters = useMemo(
() => {
return Object.fromEntries(
Object.entries(filters)
.flatMap( ([key, { get }]) => {
if ( key === 'name' ) {
const groups = [];
const nonGroups = [];
get.forEach((entry) => {
if ( entry.startsWith('GROUP:') ) {
groups.push(entry.slice(6));
} else {
nonGroups.push(entry);
}
});
return [
[key, get],
['_groups', groups],
['_companies', nonGroups],
];
} else {
return [
[key, get],
];
}
})
);
},
() => extractCurrentFilters(filters),
[filters]
);

Expand Down

0 comments on commit 5556c6f

Please sign in to comment.