Skip to content

Commit

Permalink
test(protocol-designer): add migration + load-save e2e tests (#5369)
Browse files Browse the repository at this point in the history
closes #5123 and closes #5173
  • Loading branch information
IanLondon authored Apr 8, 2020
1 parent 12277df commit b65b6a2
Show file tree
Hide file tree
Showing 16 changed files with 18,514 additions and 64 deletions.
146 changes: 146 additions & 0 deletions protocol-designer/cypress/integration/migrations.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import 'cypress-file-upload'
import cloneDeep from 'lodash/cloneDeep'
import { expectDeepEqual } from '../utils'

describe('Protocol fixtures migrate and match snapshots', () => {
beforeEach(() => {
cy.visit('/')
cy.closeAnnouncementModal()
})

const testCases = [
{
title: 'preFlexGrandfatheredProtocol 1.0.0 -> 4.0.x, schema 3',
importFixture:
'../../fixtures/protocol/1/preFlexGrandfatheredProtocol.json',
expectedExportFixture:
'../../fixtures/protocol/3/preFlexGrandfatheredProtocolMigratedFromV1_0_0.json',
newLabwareDefsMigrationModal: true,
unusedPipettes: false,
apiUpdateRequiredMigrationModal: false,
genericMigrationModal: false,
},
{
title: 'example_1_1_0 -> 4.0.x, schema 3',
importFixture: '../../fixtures/protocol/1/example_1_1_0.json',
expectedExportFixture:
'../../fixtures/protocol/3/example_1_1_0MigratedFromV1_0_0.json',
newLabwareDefsMigrationModal: true,
unusedPipettes: true,
apiUpdateRequiredMigrationModal: false,
genericMigrationModal: false,
},
{
title: 'doItAllV3 -> import and re-export should preserve data',
importFixture: '../../fixtures/protocol/3/doItAllV3.json',
expectedExportFixture: '../../fixtures/protocol/3/doItAllV3.json',
newLabwareDefsMigrationModal: false,
unusedPipettes: false,
apiUpdateRequiredMigrationModal: false,
genericMigrationModal: false,
},
{
title: 'doItAllV4 -> import and re-export should preserve data',
importFixture: '../../fixtures/protocol/4/doItAllV4.json',
expectedExportFixture: '../../fixtures/protocol/4/doItAllV4.json',
newLabwareDefsMigrationModal: false,
unusedPipettes: false,
apiUpdateRequiredMigrationModal: true,
genericMigrationModal: false,
},
]

testCases.forEach(
({
title,
importFixture,
expectedExportFixture,
newLabwareDefsMigrationModal,
unusedPipettes,
apiUpdateRequiredMigrationModal,
genericMigrationModal,
}) => {
it(title, () => {
cy.fixture(importFixture).then(fileContent => {
// TODO(IL, 2020-04-02): `cy.fixture` always parses .json files, though we want the plain text.
// So we have to use JSON.stringify. See https://github.com/cypress-io/cypress/issues/5395
// Also, the latest version v4 of cypress-file-upload is too implicit to allow us to
// use the JSON.stringify workaround, so we're stuck on 3.5.3,
// see https://github.com/abramenal/cypress-file-upload/issues/175
cy.get('input[type=file]').upload({
fileContent: JSON.stringify(fileContent),
fileName: 'fixture.json',
mimeType: 'application/json',
encoding: 'utf8',
})
})

if (genericMigrationModal) {
cy.get('div')
.contains(
'Your protocol was made in an older version of Protocol Designer'
)
.should('exist')
cy.get('button')
.contains('ok', { matchCase: false })
.click()
}

if (newLabwareDefsMigrationModal) {
// close migration announcement modal
cy.get('div')
.contains('Update protocol to use new labware definitions')
.should('exist')
cy.get('button')
.contains('update protocol', { matchCase: false })
.click()
}

cy.fixture(expectedExportFixture).then(expectedExportProtocol => {
cy.get('button')
.contains('Export')
.click()

if (unusedPipettes) {
cy.get('div')
.contains('Unused pipette')
.should('exist')
cy.get('button')
.contains('continue with export', { matchCase: false })
.click()
}

if (apiUpdateRequiredMigrationModal) {
cy.get('div')
.contains(
'Robot requirements for running module inclusive JSON protocols'
)
.should('exist')
cy.get('button')
.contains('continue', { matchCase: false })
.click()
}

cy.window().then(window => {
const savedFile = cloneDeep(window.__lastSavedFile__)
const expected = cloneDeep(expectedExportProtocol)

assert.match(
savedFile.designerApplication.version,
/^4\.0\.\d+$/,
'designerApplication.version is 4.0.x'
)
;[savedFile, expected].forEach(f => {
// Homogenize fields we don't want to compare
f.metadata.lastModified = 123
f.designerApplication.data._internalAppBuildDate = 'Foo Date'
f.designerApplication.version = 'x.x.x'
})

expectDeepEqual(savedFile, expected)
})
})
})
}
)
})
26 changes: 14 additions & 12 deletions protocol-designer/cypress/integration/settings.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
describe('The Settings Page', () => {
const exptlSettingText = 'Disable module placement restrictions'

before(() => {
cy.visit('/')
})
Expand Down Expand Up @@ -68,14 +70,14 @@ describe('The Settings Page', () => {
.should('exist')
})

it("contains a 'enable multi gen2 pipettes' button in the pivacy section", () => {
it("contains a 'disable module placement restrictions' experimental feature", () => {
// It's toggled off by default
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.should('have.attr', 'class')
.and('match', /toggled_off/)
// Click it
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.click()
// We have to confirm this one
Expand All @@ -91,24 +93,24 @@ describe('The Settings Page', () => {
.contains('Cancel')
.click()
// Still toggled off
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.should('have.attr', 'class')
.and('match', /toggled_off/)
// Click it again and confirm
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.click()
cy.get('button')
.contains('Continue')
.click()
// Now it's toggled on
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.should('have.attr', 'class')
.and('match', /toggled_on/)
// Click it again
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.click()
// We have to confirm to turn it off?
Expand All @@ -117,7 +119,7 @@ describe('The Settings Page', () => {
.contains('Continue')
.click()
// Now it's toggled off again
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.should('have.attr', 'class')
.and('match', /toggled_off/)
Expand All @@ -128,7 +130,7 @@ describe('The Settings Page', () => {
// We're not using the privacy button because that
// interacts with analytics libraries, which might
// not be accessible in a headless environment
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.click()
cy.get('button')
Expand All @@ -141,7 +143,7 @@ describe('The Settings Page', () => {
.contains('Settings')
.click()
// The toggle is still on
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.should('have.attr', 'class')
.and('match', /toggled_on/)
Expand All @@ -152,7 +154,7 @@ describe('The Settings Page', () => {
// We're not using the privacy button because that
// interacts with analytics libraries, which might
// not be accessible in a headless environment
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.click()
cy.get('button')
Expand All @@ -165,7 +167,7 @@ describe('The Settings Page', () => {
.contains('Settings')
.click()
// The toggle is still off
cy.contains('Enable multi')
cy.contains(exptlSettingText)
.next()
.should('have.attr', 'class')
.and('match', /toggled_off/)
Expand Down
33 changes: 33 additions & 0 deletions protocol-designer/cypress/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import isEqual from 'lodash/isEqual'
import isObject from 'lodash/isObject'
import transform from 'lodash/transform'

const difference = (object, base) => {
const changes = (object, base) => {
return transform(object, function(result, value, key) {
if (!isEqual(value, base[key])) {
result[key] =
isObject(value) && isObject(base[key])
? changes(value, base[key])
: value
}
})
}
return changes(object, base)
}

// deepEqual won't always return a diff, Cypress doesn't fully support object diffs :(
// also Cypress doesn't seem to support logging to the console? So throwing the diff as an error instead
export const expectDeepEqual = (a, b) => {
try {
assert.deepEqual(a, b)
} catch (e) {
// visualize undefineds
const replacer = (key, value) =>
typeof value === 'undefined' ? '__undefined__' : value
throw Error(
'Expected deep equal. Diff is: ' +
JSON.stringify(difference(a, b), replacer, 4)
)
}
}
Loading

0 comments on commit b65b6a2

Please sign in to comment.