From a4632e0368a93f9db8c68357167c0349c0a1e1b7 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 3 Mar 2020 11:53:28 -0500 Subject: [PATCH 1/3] fix(app): accept mixed-case .json extensions in custom labware Closes #5151 --- .../src/labware/__tests__/definitions.test.js | 18 +++++++++++++++++- app-shell/src/labware/definitions.js | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/app-shell/src/labware/__tests__/definitions.test.js b/app-shell/src/labware/__tests__/definitions.test.js index 378d118bc37..3a81d702dbb 100644 --- a/app-shell/src/labware/__tests__/definitions.test.js +++ b/app-shell/src/labware/__tests__/definitions.test.js @@ -39,7 +39,7 @@ describe('labware directory utilities', () => { ).rejects.toThrow(/no such file/) }) - test('returns paths to JSON files in directory', () => { + test('returns paths to *.json files in directory', () => { const dir = makeEmptyDir() return Promise.all([ @@ -76,6 +76,22 @@ describe('labware directory utilities', () => { ]) }) }) + + test('returns paths to *.JSON files in directory', () => { + const dir = makeEmptyDir() + + return Promise.all([ + fs.writeJson(path.join(dir, 'a.JSON'), { name: 'a' }), + fs.writeJson(path.join(dir, 'b.JSON'), { name: 'b' }), + fs.writeJson(path.join(dir, 'c.JSON'), { name: 'c' }), + ]).then(() => { + return expect(readLabwareDirectory(dir)).resolves.toEqual([ + path.join(dir, 'a.JSON'), + path.join(dir, 'b.JSON'), + path.join(dir, 'c.JSON'), + ]) + }) + }) }) describe('parseLabwareFiles', () => { diff --git a/app-shell/src/labware/definitions.js b/app-shell/src/labware/definitions.js index c4594a1e3ab..e0cc492a87a 100644 --- a/app-shell/src/labware/definitions.js +++ b/app-shell/src/labware/definitions.js @@ -6,6 +6,8 @@ import { shell } from 'electron' import type { Dirent } from '../types' import type { UncheckedLabwareFile } from '@opentrons/app/src/custom-labware/types' +const JSON_EXT_RE = /\.json$/i + export function readLabwareDirectory(dir: string): Promise> { const absoluteName = e => path.join(dir, e.name) @@ -13,7 +15,7 @@ export function readLabwareDirectory(dir: string): Promise> { .readdir(dir, { withFileTypes: true }) .then((entries: Array) => { const jsonFiles = entries - .filter(e => e.isFile() && e.name.endsWith('.json')) + .filter(e => e.isFile() && JSON_EXT_RE.test(e.name)) .map(absoluteName) const getNestedFiles = Promise.all( From 5e5634635b57bf4c87d86ff5b8ea4d3a6e60013e Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 3 Mar 2020 12:09:47 -0500 Subject: [PATCH 2/3] fixup: fix the same casing bug in protocol files --- app-shell/src/labware/definitions.js | 4 +-- app/src/protocol/__tests__/reducer.test.js | 42 ++++++++++++++++++++-- app/src/protocol/index.js | 1 + app/src/protocol/protocol-data.js | 17 +++++---- app/src/protocol/types.js | 4 ++- 5 files changed, 57 insertions(+), 11 deletions(-) diff --git a/app-shell/src/labware/definitions.js b/app-shell/src/labware/definitions.js index e0cc492a87a..b16b6a6613d 100644 --- a/app-shell/src/labware/definitions.js +++ b/app-shell/src/labware/definitions.js @@ -6,7 +6,7 @@ import { shell } from 'electron' import type { Dirent } from '../types' import type { UncheckedLabwareFile } from '@opentrons/app/src/custom-labware/types' -const JSON_EXT_RE = /\.json$/i +const RE_JSON_EXT = /\.json$/i export function readLabwareDirectory(dir: string): Promise> { const absoluteName = e => path.join(dir, e.name) @@ -15,7 +15,7 @@ export function readLabwareDirectory(dir: string): Promise> { .readdir(dir, { withFileTypes: true }) .then((entries: Array) => { const jsonFiles = entries - .filter(e => e.isFile() && JSON_EXT_RE.test(e.name)) + .filter(e => e.isFile() && RE_JSON_EXT.test(e.name)) .map(absoluteName) const getNestedFiles = Promise.all( diff --git a/app/src/protocol/__tests__/reducer.test.js b/app/src/protocol/__tests__/reducer.test.js index 6074673189c..c3425716410 100644 --- a/app/src/protocol/__tests__/reducer.test.js +++ b/app/src/protocol/__tests__/reducer.test.js @@ -44,7 +44,7 @@ describe('protocolReducer', () => { }, }, { - name: 'handles robot:SESSION_RESPONSE with JSON protocol', + name: 'handles robot:SESSION_RESPONSE with .json protocol', action: { type: 'robot:SESSION_RESPONSE', payload: { name: 'foo.json', protocolText: '{"metadata": {}}' }, @@ -61,7 +61,7 @@ describe('protocolReducer', () => { }, }, { - name: 'handles robot:SESSION_RESPONSE with Python protocol metadata', + name: 'handles robot:SESSION_RESPONSE with .py protocol', action: { type: 'robot:SESSION_RESPONSE', payload: { @@ -81,6 +81,44 @@ describe('protocolReducer', () => { data: { metadata: { protocolName: 'foo' } }, }, }, + { + name: 'handles robot:SESSION_RESPONSE with .JSON protocol', + action: { + type: 'robot:SESSION_RESPONSE', + payload: { name: 'foo.JSON', protocolText: '{"metadata": {}}' }, + }, + initialState: { file: null, contents: null, data: null }, + expectedState: { + file: { + name: 'foo.JSON', + type: 'json', + lastModified: null, + }, + contents: '{"metadata": {}}', + data: { metadata: {} }, + }, + }, + { + name: 'handles robot:SESSION_RESPONSE with .PY protocol', + action: { + type: 'robot:SESSION_RESPONSE', + payload: { + name: 'foo.PY', + protocolText: '# foo.py', + metadata: { protocolName: 'foo' }, + }, + }, + initialState: { file: null, contents: null, data: null }, + expectedState: { + file: { + name: 'foo.PY', + type: 'python', + lastModified: null, + }, + contents: '# foo.py', + data: { metadata: { protocolName: 'foo' } }, + }, + }, { name: 'handles robot:DISCONNECT by clearing state', action: { type: 'robot:DISCONNECT_RESPONSE' }, diff --git a/app/src/protocol/index.js b/app/src/protocol/index.js index bd58077d61e..01e2aca7524 100644 --- a/app/src/protocol/index.js +++ b/app/src/protocol/index.js @@ -15,6 +15,7 @@ import type { InvalidProtocolFileAction, } from './types' +export * from './constants' export * from './selectors' const BUNDLE_UPLOAD_DISABLED = diff --git a/app/src/protocol/protocol-data.js b/app/src/protocol/protocol-data.js index e5cf78fd0ad..dc483824c0f 100644 --- a/app/src/protocol/protocol-data.js +++ b/app/src/protocol/protocol-data.js @@ -1,15 +1,20 @@ // @flow // functions for parsing protocol files import { createLogger } from '../logger' +import { TYPE_JSON, TYPE_PYTHON, TYPE_ZIP } from './constants' import type { ProtocolFile, ProtocolData, ProtocolType } from './types' const log = createLogger(__filename) +const RE_JSON_EXT = /\.json$/i +const RE_PY_EXT = /\.py$/i +const RE_ZIP_EXT = /\.zip$/i + export function filenameToType(filename: string): ProtocolType | null { - if (filename.endsWith('.json')) return 'json' - if (filename.endsWith('.py')) return 'python' - if (filename.endsWith('.zip')) return 'zip' + if (RE_JSON_EXT.test(filename)) return TYPE_JSON + if (RE_PY_EXT.test(filename)) return TYPE_PYTHON + if (RE_ZIP_EXT.test(filename)) return TYPE_ZIP return null } @@ -44,15 +49,15 @@ export function parseProtocolData( } export function fileIsPython(file: ProtocolFile): boolean { - return file.type === 'python' || file.type == null + return file.type === TYPE_PYTHON || file.type == null } export function fileIsJson(file: ProtocolFile): boolean { - return file.type === 'json' + return file.type === TYPE_JSON } export function fileIsBundle(file: ProtocolFile): boolean { - return file.type === 'zip' + return file.type === TYPE_ZIP } export function fileIsBinary(file: ProtocolFile): boolean { diff --git a/app/src/protocol/types.js b/app/src/protocol/types.js index 2bfef3ab3f4..0672803ccf6 100644 --- a/app/src/protocol/types.js +++ b/app/src/protocol/types.js @@ -3,6 +3,8 @@ import type { ProtocolFile as SchemaV1ProtocolFile } from '@opentrons/shared-data/protocol/flowTypes/schemaV1' import type { ProtocolFile as SchemaV3ProtocolFile } from '@opentrons/shared-data/protocol/flowTypes/schemaV3' +import typeof { TYPE_JSON, TYPE_PYTHON, TYPE_ZIP } from './constants' + // data may be a full JSON protocol or just a metadata dict from Python export type ProtocolData = | SchemaV1ProtocolFile<{}> @@ -10,7 +12,7 @@ export type ProtocolData = | { metadata: $PropertyType, 'metadata'> } // NOTE: add union of additional versions after schema is bumped -export type ProtocolType = 'json' | 'python' | 'zip' +export type ProtocolType = TYPE_JSON | TYPE_PYTHON | TYPE_ZIP export type ProtocolFile = { name: string, From a7a5b41978a31b3c70e3c9aeb787013e2e05c406 Mon Sep 17 00:00:00 2001 From: Mike Cousins Date: Tue, 3 Mar 2020 13:16:02 -0500 Subject: [PATCH 3/3] fixup: actually include constants file --- app/src/protocol/constants.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 app/src/protocol/constants.js diff --git a/app/src/protocol/constants.js b/app/src/protocol/constants.js new file mode 100644 index 00000000000..afa4f697456 --- /dev/null +++ b/app/src/protocol/constants.js @@ -0,0 +1,6 @@ +// @flow + +// protocol types +export const TYPE_JSON: 'json' = 'json' +export const TYPE_PYTHON: 'python' = 'python' +export const TYPE_ZIP: 'zip' = 'zip'