Green Up: scope panel-group check to panel-backed tabs
Corporate Tools is the first PCDetail tab that mounts no PluginAssetPanels: it is permission-gated and renders a fixed action list, so no panel can land in it. The vocabulary check asserted every tabs.push key appears in RENDERED_GROUPS, which cd70556 broke by adding that tab.
Adding 'corptools' to the vocabulary would have made it a legal panel group with no panel host - fetched, held, rendered nowhere - the silent-drop failure this file exists to catch, and it would also have failed the server-vocabulary check against ASSET_PANEL_GROUPS. So the invariant is narrowed to panel-backed tabs instead, with a mirror check that any tab mounting a PluginAssetPanels must have its group rendered.
This commit is contained in:
@@ -29,12 +29,25 @@ function pcDetailSource() {
|
||||
return read('../../../plugins/computers/frontend/views/PCDetail.vue')
|
||||
}
|
||||
|
||||
// The tabs that actually HOST panels - those where PCDetail mounts a
|
||||
// PluginAssetPanels for the group. A tab is only bound by the vocabulary if a
|
||||
// panel can land in it. Corporate Tools is the first tab that hosts none: it is
|
||||
// permission-gated and renders a fixed action list, so RENDERED_GROUPS has
|
||||
// nothing to say about it and requiring it there would make 'corptools' a legal
|
||||
// panel group with no panel host - fetched, held, rendered nowhere, which is
|
||||
// the very failure this file exists to catch.
|
||||
function panelHostedGroups(source) {
|
||||
return [...source.matchAll(/group="([a-z]+)"/g)].map((match) => match[1])
|
||||
}
|
||||
|
||||
describe('panel group vocabulary', () => {
|
||||
it('renders a group for every tab PCDetail offers', () => {
|
||||
it('renders a group for every panel-backed tab PCDetail offers', () => {
|
||||
const groups = renderedGroups()
|
||||
const source = pcDetailSource()
|
||||
const hosted = panelHostedGroups(source)
|
||||
const tabKeys = [...source.matchAll(/tabs\.push\(\{\s*key:\s*'([a-z]+)'/g)]
|
||||
.map((match) => match[1])
|
||||
.filter((key) => hosted.includes(key))
|
||||
|
||||
expect(tabKeys.length).toBeGreaterThan(0)
|
||||
for (const key of tabKeys) {
|
||||
@@ -42,6 +55,19 @@ describe('panel group vocabulary', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('never hosts panels in a group this build does not render', () => {
|
||||
// The mirror of that exemption, so opting a tab out of panels cannot become
|
||||
// a loophole: the moment a tab mounts a PluginAssetPanels, its group MUST
|
||||
// be rendered, or the panels are fetched and dropped with nothing logged.
|
||||
const groups = renderedGroups()
|
||||
const hosted = panelHostedGroups(pcDetailSource())
|
||||
|
||||
expect(hosted.length).toBeGreaterThan(0)
|
||||
for (const group of hosted) {
|
||||
expect(groups).toContain(group)
|
||||
}
|
||||
})
|
||||
|
||||
it('gives every rendered group somewhere to actually show', () => {
|
||||
// A group in RENDERED_GROUPS with no v-show block is the mirror failure:
|
||||
// the panel is kept rather than dropped, but still appears nowhere.
|
||||
|
||||
Reference in New Issue
Block a user