This organization is not on your list.
${lead} ${andList(m)}.
` : ''; }; // SP3/SP4: a row's organization and project as chips, and the organizations that did not answer // named first with the reason their read gave. The Inbox and the system task board share these. - const orgChip = (o, id) => `${esc(o.name(id))}`; + // `mark`, already-escaped markup such as the organization's avatar, takes the dot's place. + const orgChip = (o, id, mark = '') => `${mark}${esc(o.name(id))}`; const projChip = name => (name ? `${esc(name)}` : ''); const unreadWhy = r => `${stateWords(r).replace(/, read .*$/, '')}${r.reason ? `: ${esc(r.reason)}` : ''}${r.at ? ` (read ${esc(hm(r.at))})` : ''}`; const unreadNote = (o, names, key) => (o.unread.length ? `Not read: ${andList(o.unread.map(unreadWhy))}.
` : '') + missLine(o, names, key); diff --git a/packages/webui/tests/system-tasks-browser.test.mjs b/packages/webui/tests/system-tasks-browser.test.mjs index 977fbd1ae..9e57c5107 100644 --- a/packages/webui/tests/system-tasks-browser.test.mjs +++ b/packages/webui/tests/system-tasks-browser.test.mjs @@ -66,6 +66,8 @@ function fixture({ empty = false } = {}) { // Runs before the page's scripts, so display.js wraps it and it sees each request's scoped URL. // Every organization's config says this Console takes task changes, except those named in `noWrite`. +// The real server sets orgSetup for the whole Console, so this per-organization difference is faked: +// it proves the board asks each row's own organization, which matters where one config read fails. const STUB = noWrite => `window.__noWrite = ${JSON.stringify(noWrite)}; (() => { const real = window.fetch.bind(window); @@ -102,8 +104,9 @@ test('#/tasks shows both organizations\' tasks on one board, each card with its assert.equal(await text('#bus-view h1'), 'Tasks'); // The per-organization page's columns. assert.deepEqual(await b.evaluate('[...document.querySelectorAll(".tb-col h3")].map(h => h.textContent)'), ['To do', 'In progress', 'Review', 'Blocked', 'Done']); - assert.equal(await text('[data-tb-task="acme|task:1"] [data-ws-orgchip]'), 'Acme Studio'); - assert.equal(await text('[data-tb-task="beta|task:2"] [data-ws-orgchip]'), 'Beta Works'); + // One chip names the organization, with its avatar in place of the dot. + assert.equal(await b.evaluate('[...document.querySelectorAll(\'[data-tb-task="acme|task:1"] [data-ws-orgchip]\')].map(c => [!!c.querySelector(".ws-av"), c.lastChild.textContent]).join("|")'), 'true,Acme Studio'); + assert.equal(await b.evaluate('[...document.querySelectorAll(\'[data-tb-task="beta|task:2"] [data-ws-orgchip]\')].map(c => [!!c.querySelector(".ws-av"), c.lastChild.textContent]).join("|")'), 'true,Beta Works'); assert.equal(await text('[data-tb-task="acme|task:1"] [data-ws-projchip]'), 'Website'); assert.equal(await text('[data-tb-task="beta|task:3"] [data-ws-projchip]'), 'Ads'); assert.equal(await b.evaluate('document.getElementById("bus-down").hidden'), true); @@ -126,6 +129,9 @@ test('the organization and project filters live in the address and survive a rel await b.navigate(`${base}/?reload=2#/tasks?org=Not%20An%20Id&project=nope`); await wait(`${cards} === ${ALL}`); assert.equal(await b.evaluate('document.querySelector("[data-tb-org=\'\']").getAttribute("aria-pressed")'), 'true'); + // An organization or project that is not on the list falls back to all, and the address drops it. + await b.navigate(`${base}/?reload=3#/tasks?org=gone&project=beta%3Agone`); + await wait(`${cards} === ${ALL} && location.hash === "#/tasks"`); }); test('a move on beta\'s row while acme was opened last reaches beta, and acme\'s own config keeps its rows read only', async t => { @@ -161,7 +167,7 @@ test('with no tasks anywhere the board says so and still names the stopped organ }); test('#/orgs/beta/tasks shows beta\'s tasks only, with a link to every organization', async t => { - const { b, wait } = await setup(t, { hash: '#/orgs/beta/tasks' }); + const { b, base, wait } = await setup(t, { hash: '#/orgs/beta/tasks' }); await wait(`${cards} === ${BETA}`); assert.equal(await b.evaluate('location.hash'), '#/orgs/beta/tasks'); assert.equal(await b.evaluate('document.querySelector("[data-tasks-board]").dataset.tbScope'), 'beta'); @@ -172,6 +178,10 @@ test('#/orgs/beta/tasks shows beta\'s tasks only, with a link to every organizat // The sidebar Tasks link opens the system board. await b.evaluate('document.querySelector("#sections a[href=\'#/tasks\']").click()'); await wait(`location.hash === "#/tasks" && ${cards} === ${ALL}`); + // An address that names no possible organization is not the system board. + await b.navigate(`${base}/?reload=1#/orgs/Not%20An%20Id/tasks`); + await wait('/not on your list/.test(document.querySelector("[data-tasks-board]")?.textContent || "")'); + assert.equal(await b.evaluate(cards), '[]'); }); test('a card opened from the system board opens the task in its organization', async t => {