-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* handlebars bootstrap * Structural implementation * Open reports page on button click * Initial styling pass * button styling * Adding JS behaviors * Prod config * Fixing pathing issue for environments * APG integration fixes * Adding condition for no data * Adding view cache * Reports sorted by AT with logic to populate columns * Remove console statement * Cleanup * Post message as object instead of string * Addressing PR feedback, adding copied message to the right, and adding window resize listener * Adding recommended notice * Removing cacheing * Make responsive (#477) * Surface AT version * Adding caching Co-authored-by: Alexander Flenniken <alflennik@users.noreply.github.com>
- Loading branch information
Showing
10 changed files
with
698 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
const express = require('express'); | ||
const { resolve } = require('path'); | ||
const { create } = require('express-handlebars'); | ||
const { | ||
ApolloClient, | ||
gql, | ||
HttpLink, | ||
InMemoryCache | ||
} = require('@apollo/client'); | ||
const fetch = require('cross-fetch'); | ||
|
||
const app = express(); | ||
const handlebarsPath = | ||
process.env.ENVIRONMENT === 'dev' ? 'handlebars' : 'server/handlebars'; | ||
|
||
// handlebars | ||
const hbs = create({ | ||
layoutsDir: resolve(`${handlebarsPath}/views/layouts`), | ||
extname: 'hbs', | ||
defaultLayout: 'index', | ||
helpers: require(resolve(`${handlebarsPath}/helpers`)) | ||
}); | ||
|
||
app.engine('hbs', hbs.engine); | ||
app.set('view engine', 'hbs'); | ||
app.set('views', resolve(`${handlebarsPath}/views`)); | ||
|
||
if (process.env.ENVIRONMENT !== 'dev') { | ||
app.enable('view cache'); | ||
} | ||
|
||
const client = new ApolloClient({ | ||
link: new HttpLink({ uri: 'http://localhost:5000/api/graphql', fetch }), | ||
cache: new InMemoryCache() | ||
}); | ||
|
||
const getLatestReportsForPattern = async pattern => { | ||
const { data } = await client.query({ | ||
query: gql` | ||
query { | ||
testPlanReports(statuses: [CANDIDATE, RECOMMENDED]) { | ||
id | ||
metrics | ||
status | ||
at { | ||
id | ||
name | ||
} | ||
browser { | ||
id | ||
name | ||
} | ||
finalizedTestResults { | ||
id | ||
atVersion { | ||
id | ||
name | ||
releasedAt | ||
} | ||
} | ||
runnableTests { | ||
id | ||
} | ||
draftTestPlanRuns { | ||
testResults { | ||
test { | ||
id | ||
} | ||
} | ||
} | ||
testPlanVersion { | ||
id | ||
updatedAt | ||
testPlan { | ||
id | ||
} | ||
} | ||
} | ||
} | ||
` | ||
}); | ||
|
||
const testPlanReports = data.testPlanReports.filter( | ||
report => report.testPlanVersion.testPlan.id === pattern | ||
); | ||
|
||
const latestTestPlanVersionId = testPlanReports.sort( | ||
(a, b) => | ||
new Date(a.testPlanVersion.updatedAt) - | ||
new Date(b.testPlanVersion.updatedAt) | ||
)[0]?.testPlanVersion.id; | ||
|
||
const latestReports = testPlanReports.filter( | ||
report => report.testPlanVersion.id === latestTestPlanVersionId | ||
); | ||
|
||
let allAts = new Set(); | ||
let allBrowsers = new Set(); | ||
let allAtVersionsByAt = {}; | ||
let status = 'RECOMMENDED'; | ||
let reportsByAt = {}; | ||
|
||
latestReports.forEach(report => { | ||
allAts.add(report.at.name); | ||
allBrowsers.add(report.browser.name); | ||
if (report.status === 'CANDIDATE') { | ||
status = report.status; | ||
} | ||
|
||
allAtVersionsByAt[report.at.name] = report.finalizedTestResults | ||
.map(result => result.atVersion) | ||
.reduce((prev, current) => | ||
new Date(prev.releasedAt) > new Date(current.releasedAt) | ||
? prev | ||
: current | ||
); | ||
}); | ||
|
||
allBrowsers = Array.from(allBrowsers).sort(); | ||
|
||
allAts.forEach(at => { | ||
reportsByAt[at] = latestReports | ||
.filter(report => report.at.name === at) | ||
.sort((a, b) => a.browser.name.localeCompare(b.browser.name)); | ||
}); | ||
|
||
return { | ||
allBrowsers, | ||
allAtVersionsByAt, | ||
latestTestPlanVersionId, | ||
status, | ||
reportsByAt | ||
}; | ||
}; | ||
|
||
app.get('/reports/:pattern', async (req, res) => { | ||
const pattern = req.params.pattern; | ||
const protocol = process.env.ENVIRONMENT === 'dev' ? 'http://' : 'https://'; | ||
const { | ||
allBrowsers, | ||
allAtVersionsByAt, | ||
latestTestPlanVersionId, | ||
status, | ||
reportsByAt | ||
} = await getLatestReportsForPattern(pattern); | ||
res.render('main', { | ||
layout: 'index', | ||
dataEmpty: Object.keys(reportsByAt).length === 0, | ||
pattern, | ||
status, | ||
allBrowsers, | ||
allAtVersionsByAt, | ||
reportsByAt, | ||
completeReportLink: `${protocol}${req.headers.host}/report/${latestTestPlanVersionId}`, | ||
embedLink: `${protocol}${req.headers.host}/embed/reports/${pattern}` | ||
}); | ||
}); | ||
|
||
app.use(express.static(resolve(`${handlebarsPath}/public`))); | ||
|
||
module.exports = app; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
let map = {}; | ||
|
||
module.exports = { | ||
isBrowser: function(a, b) { | ||
return a === b; | ||
}, | ||
isInAllBrowsers: function(value, object) { | ||
return object.allBrowsers.includes(value); | ||
}, | ||
isCandidate: function(value) { | ||
return value === 'CANDIDATE'; | ||
}, | ||
getAtVersion: function(object, key) { | ||
return object.allAtVersionsByAt[key].name; | ||
}, | ||
elementExists: function(parentObject, childObject, at, key, last) { | ||
const atBrowsers = childObject.map(o => o.browser.name); | ||
|
||
if (!map[parentObject.pattern]) { | ||
map[parentObject.pattern] = {}; | ||
} | ||
|
||
if (!(at in map[parentObject.pattern])) { | ||
map[parentObject.pattern][at] = {}; | ||
} | ||
|
||
const moreThanOneColumn = Object.values(childObject).length > 1; | ||
|
||
const conditional = | ||
moreThanOneColumn && | ||
(key in map[parentObject.pattern][at] || atBrowsers.includes(key)); | ||
|
||
// Cache columns that don't have data | ||
if ( | ||
!(key in map[parentObject.pattern][at]) && | ||
!atBrowsers.includes(key) | ||
) { | ||
map[parentObject.pattern][at][key] = true; | ||
} | ||
|
||
// Don't write to the Safari column unless it's the last element | ||
if (!last && key === 'Safari' && !atBrowsers.includes(key)) { | ||
return true; | ||
} else if (last && key === 'Safari' && !atBrowsers.includes(key)) { | ||
return false; | ||
} | ||
|
||
return conditional; | ||
}, | ||
resetMap: function() { | ||
map = {}; | ||
return; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
const iframeClass = `support-levels-${document.currentScript.getAttribute( | ||
'pattern' | ||
)}`; | ||
|
||
const iframeCode = link => | ||
` <iframe | ||
class="${iframeClass}" | ||
src="${link}" | ||
height="500" | ||
allow="clipboard-write" | ||
style="border-style: none; width: 100%;"> | ||
</iframe>`; | ||
|
||
// eslint-disable-next-line no-unused-vars | ||
const announceCopied = link => { | ||
navigator.clipboard.writeText(iframeCode(link)); | ||
const parentDiv = document.getElementById('copied-message'); | ||
const innerDiv = document.createElement('div'); | ||
const innerText = document.createTextNode('Embed link copied.'); | ||
innerDiv.appendChild(innerText); | ||
innerDiv.setAttribute('role', 'alert'); | ||
if (!parentDiv.firstChild) { | ||
parentDiv.appendChild(innerDiv); | ||
} | ||
setTimeout(() => { | ||
document.getElementById('copied-message').removeChild(innerDiv); | ||
}, 5000); | ||
}; | ||
|
||
const postHeightAndClass = () => | ||
window.parent.postMessage( | ||
{ height: document.body.scrollHeight, iframe: iframeClass }, | ||
'*' | ||
); | ||
|
||
window.onresize = postHeightAndClass; | ||
postHeightAndClass(); |
Oops, something went wrong.