Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add release notes modal #9006

Merged
merged 7 commits into from
Nov 2, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/desktop-gui/cypress/fixtures/release_notes.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"title": "Check Out This Release",
"version": "1.2.3",
"bannerImage": "https://placekitten.com/1000/200",
"content": "<h1>This is a great release</h1><h2>dolor sit amet</h2><h3>consectetur adipiscing elit</h3><h4>sed do eiusmod </h4><h5>tempor incididunt</h5><h6>ut labore et</h6><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \ntempor incididunt ut labore et dolore magna aliqua. </p><ul><li><p><b>bold</b> text</p></li><li><p><i>italic</i> text</p><ul><li><p><a href=\"https://placekitten.com/1000/200\">link</a><u></u></p></li><li><p><u>underlined</u> text</p></li></ul></li></ul><ul><li><p>some <code>code</code> here</p></li></ul><ol><li><p>one</p></li><li><p>two</p><ol><li><p>aaay</p></li><li><p>beee</p></li></ol></li><li><p>three</p></li></ol><blockquote><p>Here&#39;s a blockquote</p></blockquote><hr/><p>Duis aute irure dolor in <a href=\"https://placekitten.com/1000/200\">reprehenderit</a> in voluptate \nvelit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint \noccaecat cupidatat non proident, sunt in culpa qui officia deserunt \nmollit anim id est laborum.</p><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \ntempor incididunt ut labore et dolore magna aliqua. </p><p></p>",
chrisbreiding marked this conversation as resolved.
Show resolved Hide resolved
"externalLink": "https://example.com",
"externalLinkText": "Check out this blog post for more information"
}
135 changes: 135 additions & 0 deletions packages/desktop-gui/cypress/integration/release_notes_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { deferred } from '../support/util'

describe('Release Notes', () => {
let ipc
let releaseNotes
let getReleaseNotes

beforeEach(() => {
let user

cy.fixture('user').then((theUser) => user = theUser)
cy.fixture('release_notes').then((theReleaseNotes) => releaseNotes = theReleaseNotes)

cy.visitIndex().then((win) => {
ipc = win.App.ipc

cy.stub(ipc, 'getCurrentUser').resolves(user)
cy.stub(ipc, 'externalOpen')
cy.stub(ipc, 'getOptions').resolves({ version: '1.0.0' })
cy.stub(ipc, 'updaterCheck').resolves('1.2.3')

getReleaseNotes = deferred()
cy.stub(ipc, 'getReleaseNotes').returns(getReleaseNotes.promise)

win.App.start()
})
})

it('opens modal after clicking "Learn more" on update notice', () => {
cy.get('.update-notice').contains('Learn more').click()
cy.get('.update-modal').should('be.visible')
})

it('shows loading spinner when loading release notes', () => {
cy.get('.update-notice').contains('Learn more').click()
cy.get('.update-modal .loader')
})

it('shows update instructions if no release notes for version', () => {
getReleaseNotes.resolve(null)
cy.get('.update-notice').contains('Learn more').click()
cy.contains('Update to Version 1.2.3').should('be.visible')
})

it('shows update instructions if getting release notes errors', () => {
getReleaseNotes.reject(new Error('something went wrong'))
cy.get('.update-notice').contains('Learn more').click()
cy.contains('Update to Version 1.2.3').should('be.visible')
})

describe('when there are release notes (with all fields included)', () => {
beforeEach(() => {
getReleaseNotes.resolve(releaseNotes)
cy.get('.update-notice').contains('Learn more').click()
})

it('shows release notes title', () => {
cy.contains(releaseNotes.title)
})

it('shows banner image', () => {
cy.get('.release-notes img').should('have.attr', 'src', 'https://placekitten.com/1000/200')
cy.get('.release-notes img').should('have.attr', 'width', '548')
chrisbreiding marked this conversation as resolved.
Show resolved Hide resolved
})

it('shows content', () => {
cy.get('.release-notes .contents')
.shadow()
.find('h1')
.should('have.text', 'This is a great release')
jennifer-shehane marked this conversation as resolved.
Show resolved Hide resolved
})

it('opens links in content externally', () => {
cy.get('.release-notes .contents')
.shadow()
.find('a')
.each(($a) => {
cy.wrap($a).click().then(() => {
expect(ipc.externalOpen).to.be.calledWith($a.attr('href'))
})
})
})

it('shows update button', () => {
cy.contains('button', 'Update Now')
})

it('shows instructions when clicking update button', () => {
cy.contains('button', 'Update Now').click()
cy.contains(releaseNotes.title).should('not.exist')
cy.contains('Update to Version 1.2.3').should('be.visible')
})

it('shows external link', () => {
cy.get('.release-notes .external-link')
.scrollIntoView()
.should('be.visible')
.find('button')
.should('have.text', releaseNotes.externalLinkText)
})

it('opens external link externally', () => {
cy.get('.release-notes .external-link').click().then(() => {
expect(ipc.externalOpen).to.be.calledWith(releaseNotes.externalLink)
})
})
})

describe('when there are release notes (with some fields omitted)', () => {
beforeEach(() => {
cy.get('.update-notice').contains('Learn more').click()
})

it('does not show banner image if there is not one', () => {
releaseNotes.bannerImage = undefined
getReleaseNotes.resolve(releaseNotes)

cy.get('.release-notes img').should('not.exist')
})

it('does not show external link if link is not specified', () => {
releaseNotes.externalLink = undefined
getReleaseNotes.resolve(releaseNotes)

cy.get('.release-notes .external-link').should('not.exist')
})

it('does not show external link if link text is not specified', () => {
releaseNotes.externalLinkText = undefined
getReleaseNotes.resolve(releaseNotes)

cy.get('.release-notes .external-link').should('not.exist')
})
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import human from 'human-interval'
import { deferred } from '../support/util'

describe('Update Notice', () => {
let user
let ipc
let start
let updaterCheck

beforeEach(() => {
let user

cy.viewport(800, 500)
cy.fixture('user').then((theUser) => user = theUser)
cy.fixture('projects').as('projects')
cy.fixture('config').as('config')

cy.visitIndex().then((win) => {
ipc = win.App.ipc
Expand Down
18 changes: 11 additions & 7 deletions packages/desktop-gui/cypress/support/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,32 +28,36 @@ Cypress.Commands.add('visitIndex', (options = {}) => {
// time, so it ends up a useless white page
cy.route2({ path: /livereload/ }, '')

return cy.visit('/', options)
cy.visit('/', options)
})

Cypress.Commands.add('shouldBeOnIntro', () => {
return cy.get('.main-nav .logo')
cy.get('.main-nav .logo')
})

Cypress.Commands.add('shouldBeOnProjectSpecs', () => {
cy.contains('.folder', 'integration')

return cy.contains('.folder', 'unit')
cy.contains('.folder', 'unit')
})

Cypress.Commands.add('logOut', () => {
cy.contains('Jane Lane').click()

return cy.contains('Log Out').click()
cy.contains('Log Out').click()
})

Cypress.Commands.add('shouldBeLoggedOut', () => {
return cy.contains('.main-nav a', 'Log In')
cy.contains('.main-nav a', 'Log In')
})

Cypress.Commands.add('setAppStore', (options = {}) => {
return cy.window()
cy.window()
.then((win) => {
return win.AppStore.set(options)
if (options.version) {
win.UpdateStore.setVersion(options.version)
}

win.AppStore.set(options)
})
})
4 changes: 3 additions & 1 deletion packages/desktop-gui/src/app/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import C from '../lib/constants'
import ipc from '../lib/ipc'
import appStore from '../lib/app-store'
import authApi from '../auth/auth-api'
import updateStore from '../update/update-store'
import viewStore from '../lib/view-store'

import Intro from './intro'
Expand All @@ -20,7 +21,8 @@ class App extends Component {
appApi.listenForMenuClicks()

ipc.getOptions().then((options = {}) => {
appStore.set(_.pick(options, 'cypressEnv', 'os', 'projectRoot', 'version', 'proxySource', 'proxyServer', 'proxyBypassList'))
updateStore.setVersion(options.version)
appStore.set(_.pick(options, 'cypressEnv', 'os', 'projectRoot', 'proxySource', 'proxyServer', 'proxyBypassList'))
viewStore.showApp()
})

Expand Down
17 changes: 12 additions & 5 deletions packages/desktop-gui/src/footer/footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import React from 'react'

import ipc from '../lib/ipc'
import appStore from '../lib/app-store'
import { useUpdateChecker } from '../update/use-update-checker'
import updateStore from '../update/update-store'
import { getReleaseNotes, useUpdateChecker } from '../update/updates'

import UpdateModal from '../update/update-modal'
import UpdateNotice from '../update/update-notice'
Expand All @@ -32,20 +33,26 @@ const Footer = observer(() => {
const showModal = (e) => {
e.target.blur()

if (!appStore.updateAvailable) return
if (!updateStore.updateAvailable) return

updateStore.setState(updateStore.SHOW_INSTRUCTIONS)
state.showModal()
}

const showModalWithReleaseNotes = () => {
getReleaseNotes(updateStore.newVersion)
state.showModal()
}

return (
<footer className={cs('footer', { 'update-available': appStore.updateAvailable })}>
<button className='version' onClick={showModal} disabled={!appStore.updateAvailable}>
<footer className={cs('footer', { 'update-available': updateStore.updateAvailable })}>
<button className='version' onClick={showModal} disabled={!updateStore.updateAvailable}>
<i className='update-indicator fas fa-arrow-alt-circle-up' />
Version {appStore.displayVersion}
</button>
<button className='open-changelog' onClick={openChangelog}>Changelog</button>
<UpdateModal show={state.showingModal} onClose={state.hideModal} />
<UpdateNotice onOpenUpdatesModal={state.showModal} />
<UpdateNotice onOpenUpdatesModal={showModalWithReleaseNotes} />
</footer>
)
})
Expand Down
25 changes: 2 additions & 23 deletions packages/desktop-gui/src/lib/app-store.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { action, computed, observable } from 'mobx'
import localData from '../lib/local-data'
import updateStore from '../update/update-store'

class AppStore {
@observable cypressEnv
@observable os
@observable projectRoot = null
@observable newVersion
@observable version
@observable localInstallNoticeDismissed = localData.get('local-install-notice-dimissed')
@observable dismissedUpdateVersion = localData.get('dismissed-update-version')
@observable error
@observable proxyServer
@observable proxyBypassList
Expand All @@ -21,7 +19,7 @@ class AppStore {
}

@computed get displayVersion () {
return this.isDev ? `${this.version} (dev)` : this.version
return this.isDev ? `${updateStore.version} (dev)` : updateStore.version
}

@computed get isDev () {
Expand All @@ -32,42 +30,23 @@ class AppStore {
return !this.projectRoot
}

@computed get updateAvailable () {
return this.version !== this.newVersion
}

@computed get nonDismissedUpdateAvailable () {
return this.updateAvailable && this.newVersion !== this.dismissedUpdateVersion
}

@action set (props) {
if (props.cypressEnv != null) this.cypressEnv = props.cypressEnv

if (props.os != null) this.os = props.os

if (props.projectRoot != null) this.projectRoot = props.projectRoot

if (props.version != null) this.version = this.newVersion = props.version

this.proxyServer = props.proxyServer || this.proxyServer
this.proxyBypassList = props.proxyBypassList || this.proxyBypassList
this.proxySource = props.proxySource || this.proxySource
}

@action setNewVersion (newVersion) {
this.newVersion = newVersion
}

@action setLocalInstallNoticeDismissed (isDismissed) {
this.localInstallNoticeDismissed = isDismissed
localData.set('local-install-notice-dimissed', isDismissed)
}

@action setDismissedUpdateVersion () {
this.dismissedUpdateVersion = this.newVersion
localData.set('dismissed-update-version', this.newVersion)
}

@action setError (err) {
this.error = err
}
Expand Down
1 change: 1 addition & 0 deletions packages/desktop-gui/src/lib/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ register('get:projects')
register('get:project:statuses')
register('get:project:status')
register('get:record:keys')
register('get:release:notes')
register('get:specs', false)
register('get:user:editor')
register('set:user:editor')
Expand Down
Loading