This commit is contained in:
@@ -7976,8 +7976,30 @@ console.log('\n── Candidates vs Talent Pool ──');
|
||||
return html.slice(0, at) + html.slice(i);
|
||||
};
|
||||
|
||||
record('the layout controls are present and separable',
|
||||
now.includes('<div data-ui-controls') && stripControls(now).indexOf('data-ui-controls') === -1);
|
||||
/**
|
||||
* A page at rest draws no layout controls at all.
|
||||
*
|
||||
* This used to assert the opposite — that the controls were PRESENT — back
|
||||
* when they led with a `Customise layout` button that was on screen whether
|
||||
* or not anybody was arranging anything. That button is gone: Owliver is the
|
||||
* only way to propose a layout change now, and what is left is the bar that
|
||||
* lets a person accept or reject one. With nothing proposed and nothing
|
||||
* saved there is nothing to accept, so the component renders nothing.
|
||||
*
|
||||
* `stripControls` is kept and still exercised. The bar does appear once a
|
||||
* change is being previewed, and the comparison below must stay able to lift
|
||||
* it out when it does.
|
||||
*/
|
||||
record('a page at rest draws no layout controls',
|
||||
!now.includes('<div data-ui-controls'));
|
||||
|
||||
record('layout controls stay separable when they are drawn', (() => {
|
||||
const withBar = `<main><p>kept</p><div data-ui-controls="editor"><div><span>Apply</span></div></div><p>also kept</p></main>`;
|
||||
const stripped = stripControls(withBar);
|
||||
return !stripped.includes('data-ui-controls')
|
||||
&& stripped.includes('<p>kept</p>')
|
||||
&& stripped.includes('<p>also kept</p>');
|
||||
})());
|
||||
|
||||
now = stripControls(now);
|
||||
|
||||
@@ -8308,6 +8330,92 @@ console.log('\n── Candidates vs Talent Pool ──');
|
||||
return r.ok && ids.indexOf('timeline') < ids.indexOf('activity-privileged-notice');
|
||||
})());
|
||||
|
||||
/* ── Where "to the top" is allowed to be said ──────────────────────────── */
|
||||
|
||||
/**
|
||||
* The destination phrasings, because the missing ones read as a broken feature.
|
||||
*
|
||||
* Reported from production: "show the pipeline move to top" was answered "I
|
||||
* could not find that on this page." The target resolved perfectly well — the
|
||||
* refusal came from `planMove`, which knew `to the top` and did not know
|
||||
* `to top`, and a move with no destination falls through to `unknown`. The
|
||||
* user cannot tell that apart from the section not existing.
|
||||
*/
|
||||
const movesFirst = (q) => {
|
||||
const m = ask(q);
|
||||
if (m?.kind !== 'plan' || m.op.op !== 'reorder') return false;
|
||||
return m.op.order[0] === 'timeline';
|
||||
};
|
||||
const movesLast = (q) => {
|
||||
const m = ask(q);
|
||||
if (m?.kind !== 'plan' || m.op.op !== 'reorder') return false;
|
||||
return m.op.order[m.op.order.length - 1] === 'timeline';
|
||||
};
|
||||
|
||||
for (const q of [
|
||||
'move timeline to top',
|
||||
'move timeline to the top',
|
||||
'move the timeline to the very top',
|
||||
'move timeline up',
|
||||
'move the timeline first',
|
||||
'put the timeline at the top',
|
||||
'show the timeline move to top',
|
||||
]) {
|
||||
record(`"${q}" → timeline first`, movesFirst(q));
|
||||
}
|
||||
|
||||
for (const q of [
|
||||
'move timeline to bottom',
|
||||
'move timeline to the bottom',
|
||||
'move the timeline to the end',
|
||||
'move timeline down',
|
||||
'move the timeline last',
|
||||
]) {
|
||||
record(`"${q}" → timeline last`, movesLast(q));
|
||||
}
|
||||
|
||||
/**
|
||||
* A destination word inside a section's NAME is not a destination.
|
||||
*
|
||||
* `over` lives inside `coverage`, `end` inside `trends`. Matched as
|
||||
* substrings, "move coverage trends to the bottom" satisfied both the top
|
||||
* reading and the bottom one — and the top is tested first, so it moved the
|
||||
* opposite way from the one asked for. Whole-word matching is what fixed it;
|
||||
* this is the case that proves it.
|
||||
*/
|
||||
const coverageCase = (() => {
|
||||
const placed = opsMod4.applyOperation(tree4, {
|
||||
op: 'add', parent: null,
|
||||
node: {
|
||||
id: 'cov-1', type: 'flow', data: { source: 'candidates.activity' },
|
||||
props: { title: 'Coverage trends' },
|
||||
},
|
||||
}, { registry: reg4 });
|
||||
if (!placed.ok) return { pass: false, detail: 'fixture could not be placed' };
|
||||
|
||||
/* Put it at the TOP first, so "to the bottom" is a real change. Added at
|
||||
the end, it is already there and the answer is a refusal rather than a
|
||||
plan — which would pass this check for the wrong reason. */
|
||||
const atTop = opsMod4.applyOperation(placed.tree, {
|
||||
op: 'reorder',
|
||||
parent: null,
|
||||
order: ['cov-1', ...placed.tree.map((n) => n.id).filter((id) => id !== 'cov-1')],
|
||||
}, { registry: reg4 });
|
||||
if (!atTop.ok) return { pass: false, detail: 'fixture could not be placed first' };
|
||||
|
||||
const m = intentMod.matchUiEdit('move coverage trends to the bottom',
|
||||
{ tree: atTop.tree, registry: reg4 });
|
||||
if (m?.kind !== 'plan' || m.op.op !== 'reorder') {
|
||||
return { pass: false, detail: JSON.stringify(m) };
|
||||
}
|
||||
return {
|
||||
pass: m.op.order[m.op.order.length - 1] === 'cov-1',
|
||||
detail: m.op.order.join(', '),
|
||||
};
|
||||
})();
|
||||
record('a section named "Coverage trends" still moves to the bottom',
|
||||
coverageCase.pass, coverageCase.detail);
|
||||
|
||||
record('"Change the hiring activity to a table." → replace with table', (() => {
|
||||
const m = ask('Change the hiring activity to a table.');
|
||||
return m?.kind === 'plan' && m.op.op === 'replace' && m.op.target === 'flow-1' && m.op.type === 'table';
|
||||
|
||||
Reference in New Issue
Block a user