Skip to content

Commit

Permalink
Add confirmation button when required
Browse files Browse the repository at this point in the history
  • Loading branch information
BenSurgisonGDS committed Dec 14, 2022
1 parent 06962b1 commit d1e9362
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ describe('Management plugins: ', () => {

it(`Upgrade the ${plugin}${version1} plugin to ${plugin}${version2}`, () => {
cy.task('log', `Upgrade the ${plugin} plugin`)
cy.get(`a[href*="/upgrade?package=${encodeURIComponent(plugin)}"]`)
cy.get(`button[formaction*="/upgrade?package=${encodeURIComponent(plugin)}"]`)
.should('contains.text', 'Upgrade').click()

performPluginAction('upgrade', version2)
Expand Down Expand Up @@ -127,7 +127,7 @@ describe('Management plugins: ', () => {
it(`Uninstall the ${plugin} plugin`, () => {
cy.visit(managePluginsPagePath)
cy.task('log', `Uninstall the ${plugin} plugin`)
cy.get(`a[href*="/uninstall?package=${encodeURIComponent(plugin)}"]`)
cy.get(`button[formaction*="/uninstall?package=${encodeURIComponent(plugin)}"]`)
.should('contains.text', 'Uninstall').click()

performPluginAction('uninstall')
Expand All @@ -138,46 +138,52 @@ describe('Management plugins: ', () => {
it(`Install the ${plugin} plugin`, () => {
cy.visit(managePluginsPagePath)
cy.task('log', `Install the ${plugin} plugin`)
cy.get(`a[href*="/install?package=${encodeURIComponent(plugin)}"]`)
cy.get(`button[formaction*="/install?package=${encodeURIComponent(plugin)}"]`)
.should('contains.text', 'Install').click()

performPluginAction('install')

provePluginFunctionalityWorks()
})

it('Pass when installing a plugin already installed', () => {
cy.task('log', `Simulate refreshing the install ${plugin} plugin confirmation page`)
cy.visit(`${managePluginsPagePath}/install?package=${encodeURIComponent(plugin)}`)

performPluginAction('install')
})

it('Fail when installing a non existent plugin', () => {
const pkg = 'invalid-prototype-kit-plugin'
const pluginName = 'Invalid Prototype Kit Plugin'
cy.visit(`${managePluginsPagePath}/install?package=${encodeURIComponent(pkg)}`)
cy.get('h1')
.should('contains.text', `Install ${pluginName}`)

cy.get(panelCompleteQuery)
.should('not.be.visible')
cy.get(panelErrorQuery)
.should('not.be.visible')
cy.get(panelProcessingQuery)
.should('be.visible')
.should('contain.text', 'Installing ...')

cy.get(panelProcessingQuery, { timeout: 40000 })
.should('not.be.visible')
cy.get(panelCompleteQuery)
.should('not.be.visible')
cy.get(panelErrorQuery)
.should('be.visible')

cy.get(`${panelErrorQuery} .govuk-panel__title`)
.should('contain.text', 'There was a problem installing')
cy.get(`${panelErrorQuery} a`)
.should('contain.text', 'Please contact support')
describe('Get plugin page directly', () => {
it('Pass when installing a plugin already installed', () => {
cy.task('log', `Simulate refreshing the install ${plugin} plugin confirmation page`)
cy.visit(`${managePluginsPagePath}/install?package=${encodeURIComponent(plugin)}`)

cy.get('#plugin-action-button').click()

performPluginAction('install')
})

it('Fail when installing a non existent plugin', () => {
const pkg = 'invalid-prototype-kit-plugin'
const pluginName = 'Invalid Prototype Kit Plugin'
cy.visit(`${managePluginsPagePath}/install?package=${encodeURIComponent(pkg)}`)
cy.get('h1')
.should('contains.text', `Install ${pluginName}`)

cy.get('#plugin-action-button').click()

cy.get(panelCompleteQuery)
.should('not.be.visible')
cy.get(panelErrorQuery)
.should('not.be.visible')
cy.get(panelProcessingQuery)
.should('be.visible')
.should('contain.text', 'Installing ...')

cy.get(panelProcessingQuery, { timeout: 40000 })
.should('not.be.visible')
cy.get(panelCompleteQuery)
.should('not.be.visible')
cy.get(panelErrorQuery)
.should('be.visible')

cy.get(`${panelErrorQuery} .govuk-panel__title`)
.should('contain.text', 'There was a problem installing')
cy.get(`${panelErrorQuery} a`)
.should('contain.text', 'Please contact support')
})
})
})
71 changes: 51 additions & 20 deletions lib/assets/javascripts/manage-prototype/manage-plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ window.GOVUKPrototypeKit.documentReady(() => {
let timedOut = false
const timeout = 30 * 1000

const show = (id) => document.getElementById(id)
.classList.remove('govuk-!-display-none')

const hide = (id) => document.getElementById(id)
.classList.add('govuk-!-display-none')

const showCompleteStatus = () => {
clearTimeout(actionTimeoutId)
const panelProcessing = document.getElementById('panel-processing')
panelProcessing.classList.add('govuk-!-display-none')
const panelComplete = document.getElementById('panel-complete')
panelComplete.classList.remove('govuk-!-display-none')
const instructionsComplete = document.getElementById('instructions-complete')
instructionsComplete.classList.remove('govuk-!-display-none')
hide('panel-processing')
show('panel-complete')
show('instructions-complete')
}

const showErrorStatus = () => {
const panelProcessing = document.getElementById('panel-processing')
panelProcessing.classList.add('govuk-!-display-none')
const panelError = document.getElementById('panel-error')
panelError.classList.remove('govuk-!-display-none')
hide('panel-processing')
show('panel-error')
}

const postRequest = (url) => fetch(url, {
Expand Down Expand Up @@ -67,15 +68,45 @@ window.GOVUKPrototypeKit.documentReady(() => {
}
}

postRequest(`/manage-prototype/plugins/${mode}`)
.then(response => response.json())
.then(data => {
if (data.status === 'completed') {
// Command has already been run
showCompleteStatus()
} else {
startTimestamp = data.startTimestamp
pollStatus()
}
const performAction = (event) => {
if (event) {
event.preventDefault()
}

const panelProcessing = document.getElementById('panel-processing')
panelProcessing.classList.remove('govuk-!-display-none')

postRequest(`/manage-prototype/plugins/${mode}`)
.then(response => response.json())
.then(data => {
switch (data.status) {
case 'completed': {
// Command has already been run
showCompleteStatus()
break
}
case 'processing': {
startTimestamp = data.startTimestamp
pollStatus()
break
}
default: {
// Default to error
showErrorStatus()
}
}
})
}

const actionButton = document.getElementById('plugin-action-button')

if (actionButton) {
actionButton.addEventListener('click', (event) => {
event.preventDefault()
actionButton.classList.add('govuk-!-display-none')
performAction()
})
} else {
performAction()
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@

<div class="js-visible" aria-live="polite">

<div class="panel-processing govuk-panel govuk-panel--confirmation" id="panel-processing">
{% if not isSameOrigin %}
<button id="plugin-action-button" class="govuk-button" data-module="govuk-button">{{ verb.title }}</button>
{% endif %}

<div class="panel-processing govuk-panel govuk-panel--confirmation govuk-!-display-none" id="panel-processing">
<div class="govuk-panel__title govuk-!-font-size-27">
{{ verb.progressive|capitalize }} ...
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
{% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %}

{% block content %}
<form method="post" action="/manage-prototype/plugins">
<div class="govuk-grid-row">
<div class="govuk-grid-column-two-thirds-from-desktop">

Expand Down Expand Up @@ -33,19 +34,19 @@
</span>
<span class="govuk-prototype-kit-manage-prototype-plugin-list-plugin-list__item-buttons">
{% if not plugin.installedVersion %}
<a class="govuk-button govuk-button--secondary"
<button id="install-{{ plugin.packageName }}" class="govuk-button govuk-button--secondary"
data-module="govuk-button"
href="{{ plugin.installLink }}">Install<span class="govuk-visually-hidden"> {{ plugin.name }}</span></a>
formaction="{{ plugin.installLink }}">Install<span class="govuk-visually-hidden"> {{ plugin.name }}</span></button>
{% endif %}
{% if plugin.uninstallLink %}
<a class="govuk-button govuk-button--secondary"
<button id="uninstall-{{ plugin.packageName }}" class="govuk-button govuk-button--secondary"
data-module="govuk-button"
href="{{ plugin.uninstallLink }}">Uninstall<span class="govuk-visually-hidden"> {{ plugin.name }}</span></a>
formaction="{{ plugin.uninstallLink }}">Uninstall<span class="govuk-visually-hidden"> {{ plugin.name }}</span></button>
{% endif %}
{% if plugin.upgradeLink %}
<a class="govuk-button govuk-button--secondary"
<button id="upgrade-{{ plugin.packageName }}" class="govuk-button govuk-button--secondary"
data-module="govuk-button"
href="{{ plugin.upgradeLink }}">Upgrade<span class="govuk-visually-hidden"> {{ plugin.name }}</span></a>
formaction="{{ plugin.upgradeLink }}">Upgrade<span class="govuk-visually-hidden"> {{ plugin.name }}</span></button>
{% endif %}
{% if plugin.helpLink %}
<a class=""
Expand All @@ -58,4 +59,5 @@
{% endfor %}
</div>
</div>
</form>
{% endblock %}
102 changes: 63 additions & 39 deletions lib/routes/prototype-admin-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,26 +413,24 @@ const getCommand = (mode, chosenPlugin) => {
}
}

const getVerb = (mode) => {
switch (mode) {
case 'upgrade': return {
title: 'Upgrade',
para: 'upgrade',
status: 'upgraded',
progressive: 'upgrading'
}
case 'install': return {
title: 'Install',
para: 'install',
status: 'installed',
progressive: 'installing'
}
case 'uninstall': return {
title: 'Uninstall',
para: 'uninstall',
status: 'uninstalled',
progressive: 'uninstalling'
}
const verbs = {
upgrade: {
title: 'Upgrade',
para: 'upgrade',
status: 'upgraded',
progressive: 'upgrading'
},
install: {
title: 'Install',
para: 'install',
status: 'installed',
progressive: 'installing'
},
uninstall: {
title: 'Uninstall',
para: 'uninstall',
status: 'uninstalled',
progressive: 'uninstalling'
}
}

Expand All @@ -450,7 +448,7 @@ router.get('/plugins', async (req, res) => {
model.successBanner = {
package: extensions.preparePackageNameForDisplay(req.query.package),
mode: req.query.mode,
verb: getVerb(req.query.mode).status
verb: verbs[req.query.mode].status
}
}
model.groupsOfPlugins = await prepareForPluginPage()
Expand All @@ -459,8 +457,10 @@ router.get('/plugins', async (req, res) => {

async function getPluginForRequest (req) {
const searchPackage = req.query.package || req.body.package
return (await getPluginDetails())
.filter(({ packageName }) => packageName === searchPackage)[0]
if (searchPackage) {
return (await getPluginDetails())
.filter(({ packageName }) => packageName === searchPackage)[0]
}
}

function modeIsComplete (mode, { installedVersion, latestVersion }) {
Expand All @@ -472,7 +472,15 @@ function modeIsComplete (mode, { installedVersion, latestVersion }) {
}

router.get('/plugins/:mode', async (req, res) => {
const verb = getVerb(req.params.mode)
const isSameOrigin = req.headers['sec-fetch-site'] === 'same-origin'
const { mode } = req.params
const verb = verbs[mode]

if (!verb) {
res.status(404).send(`Page not found: ${req.path}`)
return
}

const chosenPlugin = await getPluginForRequest(req) || preparePackageNameForDisplay(req.query.package)

const pageName = `${verb.title} ${chosenPlugin.name}`
Expand All @@ -482,8 +490,9 @@ router.get('/plugins/:mode', async (req, res) => {
currentUrl: req.originalUrl,
links: managementLinks,
chosenPlugin,
command: getCommand(req.params.mode, chosenPlugin),
verb
command: getCommand(mode, chosenPlugin),
verb,
isSameOrigin
})
})

Expand All @@ -502,23 +511,38 @@ router.post('/plugins/status', async (req, res) => {
res.json({ startTimestamp, status })
})

router.post('/plugins/:mode', async (req, res, next) => {
if (req.headers['content-type'].indexOf('json') === -1) {
res.redirect(req.originalUrl)
} else {
next()
}
})

router.post('/plugins/:mode', async (req, res) => {
const verb = verbs[req.params.mode]

if (!verb) {
res.json({ startTimestamp, status: 'error' })
return
}

let status = 'processing'
try {
const chosenPlugin = await getPluginForRequest(req)
if (chosenPlugin) {
if (modeIsComplete(req.params.mode, chosenPlugin)) {
status = 'completed'
} else {
const command = getCommand(req.params.mode, chosenPlugin)
exec(command, { cwd: projectDir }).finally(() => {
console.log(`Completed ${command}`)
// force the application to stop after a delay as nodemon restart does not always work on Windows when running acceptance tests
setTimeout(() => {
process.exit(1)
}, 6000)
})
}
if (!chosenPlugin) {
status = 'error'
} else if (modeIsComplete(req.params.mode, chosenPlugin)) {
status = 'completed'
} else {
const command = getCommand(req.params.mode, chosenPlugin)
exec(command, { cwd: projectDir }).finally(() => {
console.log(`Completed ${command}`)
// force the application to stop after a delay as nodemon restart does not always work on Windows when running acceptance tests
setTimeout(() => {
process.exit(1)
}, 6000)
})
}
} catch (e) {
console.log(e)
Expand Down

0 comments on commit d1e9362

Please sign in to comment.