diff --git a/tests/tools/page-status/status.test.js b/tests/tools/page-status/status.test.js new file mode 100644 index 00000000..614eb264 --- /dev/null +++ b/tests/tools/page-status/status.test.js @@ -0,0 +1,104 @@ +import { describe, it } from 'node:test'; +import assert from 'node:assert/strict'; +import classifySequenceStatus from '../../../tools/page-status/status.js'; + +const EDIT = '2024-01-01T00:00:00.000Z'; +const PREVIEW = '2024-01-02T00:00:00.000Z'; +const PUBLISH = '2024-01-03T00:00:00.000Z'; + +describe('classifySequenceStatus — with source date', () => { + it('returns Not previewed when only edit exists', () => { + const { label, positive } = classifySequenceStatus(EDIT, undefined, undefined); + assert.equal(label, 'Not previewed'); + assert.equal(positive, true); + }); + + it('returns Not published when edit and preview exist in sequence', () => { + const { label, positive } = classifySequenceStatus(EDIT, PREVIEW, undefined); + assert.equal(label, 'Not published'); + assert.equal(positive, true); + }); + + it('returns Pending changes when edit is newer than preview (unpublished edit)', () => { + const { label, positive } = classifySequenceStatus(PUBLISH, EDIT, undefined); + assert.equal(label, 'Pending changes'); + assert.equal(positive, true); + }); + + it('returns Current when edit → preview → publish are in sequence', () => { + const { label, positive } = classifySequenceStatus(EDIT, PREVIEW, PUBLISH); + assert.equal(label, 'Current'); + assert.equal(positive, true); + }); + + it('returns Pending changes when preview is newer than publish', () => { + const { label, positive } = classifySequenceStatus(EDIT, PUBLISH, PREVIEW); + assert.equal(label, 'Pending changes'); + assert.equal(positive, true); + }); +}); + +describe('classifySequenceStatus — no source for authored content', () => { + [undefined, null, 'not-a-date'].forEach((noSource) => { + const label = noSource === null ? 'null' : String(noSource); + + it(`preserves No source warning when only preview exists [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, PREVIEW, undefined); + assert.equal(result.label, 'No source'); + assert.equal(result.positive, false); + }); + + it(`preserves No source warning when preview and publish exist [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, PREVIEW, PUBLISH); + assert.equal(result.label, 'No source'); + assert.equal(result.positive, false); + }); + }); +}); + +describe('classifySequenceStatus — no source (BYOM, issue #340)', () => { + // Both undefined (absent field) and null (explicit API value) must be treated as no source. + [undefined, null].forEach((noSource) => { + const label = noSource === null ? 'null' : 'undefined'; + + it(`returns No source (negative) when nothing exists [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, undefined, undefined, { + allowMissingSourceDate: true, + }); + assert.equal(result.label, 'No source'); + assert.equal(result.positive, false); + }); + + it(`returns Not published (positive) when only preview exists [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, PREVIEW, undefined, { + allowMissingSourceDate: true, + }); + assert.equal(result.label, 'Not published'); + assert.equal(result.positive, true); + }); + + it(`returns Current (positive) when only publish exists [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, undefined, PUBLISH, { + allowMissingSourceDate: true, + }); + assert.equal(result.label, 'Current'); + assert.equal(result.positive, true); + }); + + it(`returns Current (positive) when preview and publish are in sequence [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, PREVIEW, PUBLISH, { + allowMissingSourceDate: true, + }); + assert.equal(result.label, 'Current'); + assert.equal(result.positive, true); + }); + + it(`returns Pending changes (positive) when preview is newer than publish [edit=${label}]`, () => { + const result = classifySequenceStatus(noSource, PUBLISH, PREVIEW, { + allowMissingSourceDate: true, + }); + assert.equal(result.label, 'Pending changes'); + assert.equal(result.positive, true); + }); + }); +}); diff --git a/tools/page-status/scripts.js b/tools/page-status/scripts.js index 6df71010..8cfa079b 100644 --- a/tools/page-status/scripts.js +++ b/tools/page-status/scripts.js @@ -3,6 +3,7 @@ import { decorateIcons } from '../../scripts/aem.js'; import { initConfigField, updateConfig } from '../../utils/config/config.js'; import { ensureLogin } from '../../blocks/profile/profile.js'; import loadingMessages from './loading-messages.js'; +import classifySequenceStatus from './status.js'; const FORM = document.getElementById('status-form'); const TABLE = document.querySelector('table'); @@ -318,38 +319,21 @@ function buildLink(text, url, path) { * @param {string} publish - Publish date. * @returns {HTMLSpanElement} Status light element indicating status and sequence. */ -function buildSequenceStatus(edit, preview, publish) { - // check if a date is valid - const date = (d) => !Number.isNaN(d.getTime()); - const editDate = new Date(edit); - const previewDate = new Date(preview); - const publishDate = new Date(publish); - const inSequence = (editDate <= previewDate && previewDate <= publishDate); +function buildSequenceStatus(edit, preview, publish, options) { + const { label, positive } = classifySequenceStatus(edit, preview, publish, options); const span = document.createElement('span'); span.className = 'status-light'; - let status; - if (!date(editDate)) { - status = 'No source'; - span.classList.add('negative'); - } else if (date(editDate) && !date(previewDate) && !date(publishDate)) { - status = 'Not previewed'; - span.classList.add('positive'); - } else if ( - date(editDate) - && date(previewDate) - && !date(publishDate) - && editDate <= previewDate - ) { - status = 'Not published'; - span.classList.add('positive'); - } else { - status = inSequence ? 'Current' : 'Pending changes'; - span.classList.add('positive'); - } - span.textContent = status; + span.classList.add(positive ? 'positive' : 'negative'); + span.textContent = label; return span; } +function isBYOMContentSource(contentSource) { + const { type, url } = contentSource || {}; + if (type !== 'markup') return false; + return !url?.startsWith('https://content.da.live') && !url?.includes('adobeaemcloud'); +} + function buildRedirectIcon(redirectLocation) { if (!redirectLocation) return ''; @@ -387,7 +371,7 @@ function buildRedirectIcon(redirectLocation) { * @param {string} resource.path - The resource's path. * @returns {HTMLTableRowElement|null} `