From cb8aaf9cec283e2ffac79977af31b8ed9386c1e0 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Mon, 2 Dec 2019 12:32:49 -0800 Subject: [PATCH 1/3] Sorting keys wherever we render inputs/outputs --- src/common/utils.ts | 14 ++++++ .../useLaunchWorkflowFormState.ts | 3 +- src/components/Literals/LiteralMapViewer.tsx | 3 +- .../Literals/Scalar/ProtobufStructValue.tsx | 3 +- .../Scalar/test/ProtobufStructValue.test.tsx | 25 +++++++++++ .../Literals/test/LiteralMapViewer.test.tsx | 21 +++++++++ src/components/Task/SimpleTaskInterface.tsx | 10 +++-- .../Task/test/SimpleTaskInterface.test.tsx | 45 +++++++++++++++++++ 8 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx create mode 100644 src/components/Literals/test/LiteralMapViewer.test.tsx create mode 100644 src/components/Task/test/SimpleTaskInterface.test.tsx diff --git a/src/common/utils.ts b/src/common/utils.ts index 080d69476..d9f0ab36a 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -85,3 +85,17 @@ export function createCorsProxyURL(path: string) { `${baseUrl}${corsProxyPrefix}${ensureSlashPrefixed(path)}` ); } + +/** Returns entires for an object, sorted lexographically */ +export function sortedObjectEntries( + object: Object +): ReturnType { + return Object.entries(object).sort((a, b) => a[0].localeCompare(b[0])); +} + +/** Returns keys for an objext, sorted lexographically */ +export function sortedObjectKeys( + object: Object +): ReturnType { + return Object.keys(object).sort((a, b) => a.localeCompare(b)); +} diff --git a/src/components/Launch/LaunchWorkflowForm/useLaunchWorkflowFormState.ts b/src/components/Launch/LaunchWorkflowForm/useLaunchWorkflowFormState.ts index 1a2dfa45e..ea7ed1f2b 100644 --- a/src/components/Launch/LaunchWorkflowForm/useLaunchWorkflowFormState.ts +++ b/src/components/Launch/LaunchWorkflowForm/useLaunchWorkflowFormState.ts @@ -1,3 +1,4 @@ +import { sortedObjectEntries } from 'common/utils'; import { useAPIContext } from 'components/data/apiContext'; import { useFetchableData, @@ -53,7 +54,7 @@ function getInputs(workflow: Workflow, launchPlan: LaunchPlan): ParsedInput[] { const workflowInputs = getWorkflowInputs(workflow); const launchPlanInputs = launchPlan.closure.expectedInputs.parameters; - return Object.entries(launchPlanInputs).map(value => { + return sortedObjectEntries(launchPlanInputs).map(value => { const [name, parameter] = value; const required = !!(parameter.default || parameter.required); const workflowInput = workflowInputs[name]; diff --git a/src/components/Literals/LiteralMapViewer.tsx b/src/components/Literals/LiteralMapViewer.tsx index 7f2d85ab0..41965a6bb 100644 --- a/src/components/Literals/LiteralMapViewer.tsx +++ b/src/components/Literals/LiteralMapViewer.tsx @@ -1,4 +1,5 @@ import * as classnames from 'classnames'; +import { sortedObjectEntries } from 'common/utils'; import { useCommonStyles } from 'components/common/styles'; import { Literal, LiteralMap } from 'models'; import * as React from 'react'; @@ -22,7 +23,7 @@ export const LiteralMapViewer: React.FC<{ commonStyles.listUnstyled )} > - {Object.entries(literals).map(([key, value]) => ( + {sortedObjectEntries(literals).map(([key, value]) => (
  • diff --git a/src/components/Literals/Scalar/ProtobufStructValue.tsx b/src/components/Literals/Scalar/ProtobufStructValue.tsx index 9cae296c7..7f2f9cff8 100644 --- a/src/components/Literals/Scalar/ProtobufStructValue.tsx +++ b/src/components/Literals/Scalar/ProtobufStructValue.tsx @@ -1,4 +1,5 @@ import * as classnames from 'classnames'; +import { sortedObjectEntries } from 'common/utils'; import { useCommonStyles } from 'components/common/styles'; import { ProtobufListValue, ProtobufStruct, ProtobufValue } from 'models'; import * as React from 'react'; @@ -63,7 +64,7 @@ export const ProtobufStructValue: React.FC<{ commonStyles.listUnstyled )} > - {Object.entries(fields).map(([key, value]) => ( + {sortedObjectEntries(fields).map(([key, value]) => (
  • diff --git a/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx b/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx new file mode 100644 index 000000000..eef7990c6 --- /dev/null +++ b/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx @@ -0,0 +1,25 @@ +import { render } from '@testing-library/react'; +import * as React from 'react'; + +import { ProtobufStruct } from 'models'; +import { ProtobufStructValue } from '../ProtobufStructValue'; + +describe('Scalars/ProtobufStructValue', () => { + it('renders sorted keys', () => { + const struct: ProtobufStruct = { + fields: { + input2: { + kind: 'nullValue' + }, + input1: { kind: 'nullValue' } + } + }; + const { getAllByText } = render( + + ); + const labels = getAllByText(/input/); + expect(labels.length).toBe(2); + expect(labels[0]).toHaveTextContent(/input1/); + expect(labels[1]).toHaveTextContent(/input2/); + }); +}); diff --git a/src/components/Literals/test/LiteralMapViewer.test.tsx b/src/components/Literals/test/LiteralMapViewer.test.tsx new file mode 100644 index 000000000..ea9e429e5 --- /dev/null +++ b/src/components/Literals/test/LiteralMapViewer.test.tsx @@ -0,0 +1,21 @@ +import { render } from '@testing-library/react'; +import * as React from 'react'; + +import { LiteralMap } from 'models'; +import { LiteralMapViewer } from '../LiteralMapViewer'; + +describe('Literals/LiteralMapViewer', () => { + it('renders sorted keys', () => { + const literals: LiteralMap = { + literals: { + input2: {}, + input1: {} + } + }; + const { getAllByText } = render(); + const labels = getAllByText(/input/); + expect(labels.length).toBe(2); + expect(labels[0]).toHaveTextContent(/input1/); + expect(labels[1]).toHaveTextContent(/input2/); + }); +}); diff --git a/src/components/Task/SimpleTaskInterface.tsx b/src/components/Task/SimpleTaskInterface.tsx index 01260f2ec..9191123e8 100644 --- a/src/components/Task/SimpleTaskInterface.tsx +++ b/src/components/Task/SimpleTaskInterface.tsx @@ -1,5 +1,6 @@ import { makeStyles, Theme } from '@material-ui/core/styles'; import { noneString } from 'common/constants'; +import { sortedObjectKeys } from 'common/utils'; import { DetailsGroup } from 'components/common'; import { useCommonStyles } from 'components/common/styles'; import { @@ -30,11 +31,11 @@ const VariablesList: React.FC<{ variables: Record }> = ({ }) => { const commonStyles = useCommonStyles(); const styles = useStyles(); - const output = Object.keys(variables).reduce( + const output = sortedObjectKeys(variables).reduce( (out, name, idx) => { const variable = variables[name]; out.push( - + {idx > 0 ? ', ' : ''} {name} @@ -44,7 +45,10 @@ const VariablesList: React.FC<{ variables: Record }> = ({ ); if (typeString.length > 0) { out.push( - + ( {typeString} ) diff --git a/src/components/Task/test/SimpleTaskInterface.test.tsx b/src/components/Task/test/SimpleTaskInterface.test.tsx new file mode 100644 index 000000000..3953fcaf7 --- /dev/null +++ b/src/components/Task/test/SimpleTaskInterface.test.tsx @@ -0,0 +1,45 @@ +import { render } from '@testing-library/react'; +import { set } from 'lodash'; +import * as React from 'react'; + +import { SimpleType, Task, TypedInterface, Variable } from 'models'; +import { SimpleTaskInterface } from '../SimpleTaskInterface'; + +function setTaskInterface(task: Task, values: TypedInterface): Task { + return set(task, 'closure.compiledTask.template.interface', values); +} + +describe('SimpleTaskInterface', () => { + const values: Record = { + value2: { type: { simple: SimpleType.INTEGER } }, + value1: { type: { simple: SimpleType.INTEGER } } + }; + + it('renders sorted inputs', () => { + const task = setTaskInterface({} as Task, { + inputs: { variables: { ...values } } + }); + + const { getAllByText } = render( + + ); + const labels = getAllByText(/value/); + expect(labels.length).toBe(2); + expect(labels[0]).toHaveTextContent(/value1/); + expect(labels[1]).toHaveTextContent(/value2/); + }); + + it('renders sorted outputs', () => { + const task = setTaskInterface({} as Task, { + outputs: { variables: { ...values } } + }); + + const { getAllByText } = render( + + ); + const labels = getAllByText(/value/); + expect(labels.length).toBe(2); + expect(labels[0]).toHaveTextContent(/value1/); + expect(labels[1]).toHaveTextContent(/value2/); + }); +}); From 9daac9361717bd8567470245e9761c951e4b1cb9 Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Mon, 2 Dec 2019 12:33:39 -0800 Subject: [PATCH 2/3] Removing unnecessary scoping for tests --- .../Literals/Scalar/test/ProtobufStructValue.test.tsx | 2 +- src/components/Literals/test/LiteralMapViewer.test.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx b/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx index eef7990c6..fe10b91c9 100644 --- a/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx +++ b/src/components/Literals/Scalar/test/ProtobufStructValue.test.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { ProtobufStruct } from 'models'; import { ProtobufStructValue } from '../ProtobufStructValue'; -describe('Scalars/ProtobufStructValue', () => { +describe('ProtobufStructValue', () => { it('renders sorted keys', () => { const struct: ProtobufStruct = { fields: { diff --git a/src/components/Literals/test/LiteralMapViewer.test.tsx b/src/components/Literals/test/LiteralMapViewer.test.tsx index ea9e429e5..ba731c5a0 100644 --- a/src/components/Literals/test/LiteralMapViewer.test.tsx +++ b/src/components/Literals/test/LiteralMapViewer.test.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { LiteralMap } from 'models'; import { LiteralMapViewer } from '../LiteralMapViewer'; -describe('Literals/LiteralMapViewer', () => { +describe('LiteralMapViewer', () => { it('renders sorted keys', () => { const literals: LiteralMap = { literals: { From 8ca22ae1e98c150210f5c4bf6c0ff9771806a68e Mon Sep 17 00:00:00 2001 From: Randy Schott <1815175+schottra@users.noreply.github.com> Date: Mon, 2 Dec 2019 12:37:31 -0800 Subject: [PATCH 3/3] typo --- src/common/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/utils.ts b/src/common/utils.ts index d9f0ab36a..e727f243b 100644 --- a/src/common/utils.ts +++ b/src/common/utils.ts @@ -86,14 +86,14 @@ export function createCorsProxyURL(path: string) { ); } -/** Returns entires for an object, sorted lexographically */ +/** Returns entires for an object, sorted lexicographically */ export function sortedObjectEntries( object: Object ): ReturnType { return Object.entries(object).sort((a, b) => a[0].localeCompare(b[0])); } -/** Returns keys for an objext, sorted lexographically */ +/** Returns keys for an objext, sorted lexicographically */ export function sortedObjectKeys( object: Object ): ReturnType {