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
19 changes: 18 additions & 1 deletion blocks/component-status/component-status.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { resolveImplementation } from '../../scripts/utils/go-to-impl.js';
import { figmaNodeUrl } from '../../scripts/utils/figma.js';
import { implAndSlugFromPath } from '../../scripts/utils/component-path.js';
import { fetchComponentSlice } from '../../scripts/utils/component-slice.js';
import { getConfig, removeForAudience } from '../../scripts/ak.js';

const NOT_AVAILABLE = 'not-available';

Expand Down Expand Up @@ -129,7 +130,23 @@ export default async function init(el) {
return;
}

const fragment = document.createDocumentFragment();
fragment.append(...pills);
const designPill = fragment.querySelector('[data-kind="design"]');
try {
await removeForAudience({ privateEl: designPill });
} catch (error) {
getConfig().log(error);
designPill?.remove();
}
const visiblePills = [...fragment.children];

if (!visiblePills.length) {
el.remove();
return;
}

el.setAttribute('role', 'group');
el.setAttribute('aria-label', 'Component status');
el.replaceChildren(...pills);
el.replaceChildren(...visiblePills);
}
170 changes: 169 additions & 1 deletion test/blocks/component-status.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,38 @@
import { expect } from '@esm-bundle/chai';
import { resolveContext, buildPills } from '../../blocks/component-status/component-status.js';
import sinon from 'sinon';
import init, { resolveContext, buildPills } from '../../blocks/component-status/component-status.js';
import { setConfig } from '../../scripts/ak.js';
import { resetComponentSliceCacheForTests } from '../../scripts/utils/component-slice.js';

const SAFE_CONFIG = {
hostnames: ['authorkit.dev'],
components: [],
locales: { '': { lang: 'en' } },
};

const setSessionHint = () => {
document.cookie = `spectrum_session_active=${Date.now() + 2 * 60 * 60 * 1000}; path=/`;
};

const clearSessionHint = () => {
document.cookie = 'spectrum_session_active=; path=/; max-age=0';
};

function waitFor(predicate, timeout = 2000) {
return new Promise((resolve, reject) => {
const start = performance.now();
const check = () => {
if (predicate()) {
resolve();
} else if (performance.now() - start > timeout) {
reject(new Error('timed out waiting for condition'));
} else {
setTimeout(check, 10);
}
};
check();
});
}

describe('component-status block', () => {
describe('resolveContext', () => {
Expand Down Expand Up @@ -106,4 +139,139 @@ describe('component-status block', () => {
expect(pills).to.have.lengthOf(2);
});
});

describe('init', () => {
let sandbox;
let originalUrl;

beforeEach(() => {
sandbox = sinon.createSandbox();
originalUrl = window.location.pathname + window.location.search + window.location.hash;
document.body.innerHTML = '';
resetComponentSliceCacheForTests();
clearSessionHint();
setConfig(SAFE_CONFIG);
delete window.adobeIMS;
delete window.adobeid;
window.history.pushState({}, '', '/web/rsp/components/action-button');
sandbox.stub(window, 'fetch').callsFake(async (url) => {
if (!url.toString().includes('/deps/status/action-button.json')) {
return { ok: false };
}
return {
ok: true,
json: async () => ({
web: {
rsp: { status: 'available' },
figma: { status: 'available' },
},
figmaPageId: '123:456',
}),
};
});
});

afterEach(() => {
sandbox.restore();
clearSessionHint();
window.history.pushState({}, '', originalUrl);
delete window.adobeIMS;
delete window.adobeid;
document.body.innerHTML = '';
});

it('removes the Design pill for an anonymous CDN visitor', async () => {
setConfig({ ...SAFE_CONFIG, cdnEnv: true });
const el = document.createElement('div');

await init(el);

expect(el.querySelector('[data-kind="dev"]')).to.exist;
expect(el.querySelector('[data-kind="design"]') === null).to.be.true;
});

it('keeps the Design pill for an authenticated CDN visitor', async () => {
setConfig({ ...SAFE_CONFIG, cdnEnv: true });
setSessionHint();
window.adobeIMS = {
getAccessToken: () => ({ token: 'test-token' }),
getProfile: async () => ({ email: 'developer@example.com' }),
};
const el = document.createElement('div');

const initPromise = init(el);
await waitFor(() => window.adobeid?.onReady);
await window.adobeid.onReady();
await initPromise;

expect(el.querySelector('[data-kind="design"]')).to.exist;
});

it('renders only Code and logs when IMS readiness fails for an active CDN session', async function test() {
this.timeout(5000);
setSessionHint();
const frame = document.createElement('iframe');
frame.src = '/test/a11y/fixtures/action-button.html';
document.body.append(frame);
await new Promise((resolve) => {
frame.addEventListener('load', resolve, { once: true });
});

frame.contentWindow.history.pushState(
{},
'',
'/web/rsp/components/action-button#access_token=test',
);
frame.contentWindow.fetch = async () => ({
ok: true,
json: async () => ({
web: {
rsp: { status: 'available' },
figma: { status: 'available' },
},
figmaPageId: '123:456',
}),
});
const setup = frame.contentDocument.createElement('script');
setup.type = 'module';
setup.textContent = `
window.__authFailureReady = (async () => {
const [{ setConfig }, { default: init }] = await Promise.all([
import('/scripts/ak.js'),
import('/blocks/component-status/component-status.js'),
]);
setConfig({
hostnames: ['authorkit.dev'],
components: [],
locales: { '': { lang: 'en' } },
cdnEnv: true,
log: (...args) => { window.__authFailureLog = args; },
});
window.__authFailureEl = document.createElement('div');
window.__authFailureInit = init(window.__authFailureEl);
})();
`;
frame.contentDocument.body.append(setup);

await waitFor(() => frame.contentWindow.__authFailureReady);
await frame.contentWindow.__authFailureReady;
await waitFor(() => frame.contentWindow.adobeid?.onError);
const failure = new frame.contentWindow.Error('IMS failed');
frame.contentWindow.adobeid.onError(failure);
await frame.contentWindow.__authFailureInit;

expect(frame.contentWindow.__authFailureEl.querySelector('[data-kind="dev"]')).to.exist;
expect(frame.contentWindow.__authFailureEl.querySelector('[data-kind="design"]')).to.be.null;
expect(frame.contentWindow.__authFailureLog).to.deep.equal([failure]);
});

it('keeps the Design pill off-CDN without authentication', async () => {
setConfig({ ...SAFE_CONFIG, cdnEnv: false });
const el = document.createElement('div');

await init(el);

expect(el.querySelector('[data-kind="design"]')).to.exist;
});
});
});
Loading