Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions app/assets/config/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//= link components/map/map-test.geojson
//= link components/map/maplibre-gl-csp-worker.js
//= link views/travel-advice.js
//= link lib/asset-manager-session.js

//= link static-error-pages.js

Expand Down
33 changes: 33 additions & 0 deletions app/assets/javascripts/lib/asset-manager-session.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Warms up the Asset Manager draft-assets session cookie via a single
* placeholder image request, then reloads any draft images already on
* the page once that request has settled (success or failure).
*/
/* istanbul ignore next */
window.GOVUK = window.GOVUK || {};

(function (root) {
function draftAssetImages () {
return [...document.images].filter((image) =>
image.src.includes('assets.') // currently, asset urls in draft preview point to their live link: 'assets.xyz' instead of 'draft-assets.xyz'. Created a backlog item for this: https://gov-uk.atlassian.net/browse/WHIT-4008.
)
}

function reloadDraftImages (draftAssets) {
draftAssets.forEach((image) => {
// re-setting src tries to fetch the image again, with the hope that a prior success is just served from cache
image.setAttribute('src', image.getAttribute('src'))
})
}

root.GOVUK.warmUpAssetManagerSession = function (placeholderAssetUrl) {
const draftAssets = draftAssetImages()
// fewer than 2 images means there's no concurrent-request race to fix, so nothing to warm up
if (draftAssets.length < 2) return

const reload = () => reloadDraftImages(draftAssets)
const placeholder = new Image()
placeholder.onload = reload
placeholder.onerror = reload
placeholder.src = placeholderAssetUrl
}
}(window))
1 change: 1 addition & 0 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
<%= yield %>
</main>
</div>
<%= render "shared/asset_manager_session" %>
<% end %>
1 change: 1 addition & 0 deletions app/views/layouts/full_width.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
<span id="Top"></span>
<%= yield %>
</main>
<%= render "shared/asset_manager_session" %>
<% end %>
1 change: 1 addition & 0 deletions app/views/layouts/header_content_sidebar.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,6 @@
</div>
<% end %>
</main>
<%= render "shared/asset_manager_session" %>
</div>
<% end %>
1 change: 1 addition & 0 deletions app/views/layouts/header_sidebar_content.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
</div>
</div>
</main>
<%= render "shared/asset_manager_session" %>
</div>
<% end %>
1 change: 1 addition & 0 deletions app/views/layouts/manual.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
<span id="Top"></span>
<%= yield %>
</main>
<%= render "shared/asset_manager_session" %>
</div>
<% end %>
18 changes: 18 additions & 0 deletions app/views/shared/_asset_manager_session.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<% if draft_host? %>
<%
host = if GovukEnvironment.current == "staging"
"staging.publishing.service.gov.uk"
elsif GovukEnvironment.current == "integration"
"integration.publishing.service.gov.uk"
else
"publishing.service.gov.uk"
end
placeholder_asset_url = "https://draft-assets.#{host}/media/5e59279b86650c53b2cefbfe/placeholder.jpg"
%>
<%= javascript_include_tag "lib/asset-manager-session.js", integrity: false %>
<%= javascript_tag nonce: true do %>
window.addEventListener("load", function () {
GOVUK.warmUpAssetManagerSession('<%= placeholder_asset_url %>')

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice.

})
<% end %>
<% end %>
90 changes: 90 additions & 0 deletions spec/javascripts/lib/asset-manager-session.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
describe('GOVUK.warmUpAssetManagerSession', function () {
let originalImage

const addImage = (src) => {
const img = document.createElement('img')
img.src = src
document.body.appendChild(img)
return img
}

const trackReloads = (image) => {
const reloadedUrls = []
const originalSetAttribute = image.setAttribute.bind(image)
image.setAttribute = (name, value) => {
if (name === 'src') reloadedUrls.push(value)
originalSetAttribute(name, value)
}
return reloadedUrls
}

const stubImage = ({ succeeds }) => {
class FakeImage {
get src () {
return this._src
}

set src (value) {
this._src = value
succeeds
? this.onload && this.onload()
: this.onerror && this.onerror()
}
}
window.Image = FakeImage
}

beforeEach(function () {
originalImage = window.Image
})

afterEach(function () {
window.Image = originalImage
document.querySelectorAll('img').forEach((img) => img.remove())
})

it('does nothing when fewer than two draft images are on the page', function () {
addImage('https://draft-assets.test.gov.uk/media/1/one.jpg')
stubImage({ succeeds: true })

GOVUK.warmUpAssetManagerSession('https://draft-assets.test.gov.uk/media/placeholder/placeholder.jpg')

expect(document.images[0].src).toEqual(
'https://draft-assets.test.gov.uk/media/1/one.jpg'
)
})

it('retries all draft images once the placeholder request succeeds', function () {
const first = addImage('https://draft-assets.test.gov.uk/media/1/one.jpg')
const second = addImage('https://draft-assets.test.gov.uk/media/2/two.jpg?foo=bar')
const firstReloads = trackReloads(first)
const secondReloads = trackReloads(second)
stubImage({ succeeds: true })

GOVUK.warmUpAssetManagerSession('https://draft-assets.test.gov.uk/media/placeholder/placeholder.jpg')

expect(firstReloads).toEqual(['https://draft-assets.test.gov.uk/media/1/one.jpg'])
expect(secondReloads).toEqual(['https://draft-assets.test.gov.uk/media/2/two.jpg?foo=bar'])
})

it('also retries all draft images when the placeholder request errors', function () {
const first = addImage('https://draft-assets.test.gov.uk/media/1/one.jpg')
addImage('https://draft-assets.test.gov.uk/media/2/two.jpg')
const firstReloads = trackReloads(first)
stubImage({ succeeds: false })

GOVUK.warmUpAssetManagerSession('https://draft-assets.test.gov.uk/media/placeholder/placeholder.jpg')

expect(firstReloads).toEqual(['https://draft-assets.test.gov.uk/media/1/one.jpg'])
})

it('ignores non-asset-manager images when counting/retrying', function () {
const other = addImage('https://static.test.gov.uk/media/1/one.jpg')
addImage('https://draft-assets.test.gov.uk/media/2/two.jpg')
stubImage({ succeeds: true })

GOVUK.warmUpAssetManagerSession('https://draft-assets.test.gov.uk/media/placeholder/placeholder.jpg')

expect(other.src).toEqual('https://static.test.gov.uk/media/1/one.jpg')
})
})