Files
gstack/test/qid-marker.test.ts
T
Garry Tanandcapy-ai[bot] 68b1d056a4 Fix five small browse and question-hook bugs (#4192)
* Keep the scroll selector when --wait is absent

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Skip a PATH browse that links to another program such as xdg-open

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Give each project its own browse slug on installs without ~/.claude

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Keep a question marker when its signal hint is malformed

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Read the lowercased question id in the never-ask preference hook

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Keep a marker's tail in the one-way door check

Review-Finding: p1/targeted#1 reader=targeted
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Keep a marker's tail apart from the next word in the one-way door check

Review-Finding: p2/outside-voice#1 reader=outside-voice
Review-Finding: p2/adversarial-native#1 reader=adversarial-native
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Say where old domain skills live and narrow the marker note in the changelog

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* Say how the browse daemon picks a project's domain skill file

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-09 06:38:42 +00:00

57 lines
2.6 KiB
TypeScript

import { describe, expect, test } from 'bun:test';
import { parseQidMarker } from '../scripts/qid-marker';
import { resolveHintedOptionKey, signalKeyLines } from '../scripts/psychographic-signals';
describe('parseQidMarker (#3442)', () => {
test('reads a plain marker and strips it', () => {
expect(parseQidMarker('D1 — Ship? <gstack-qid:ship-test-failure-triage>')).toEqual({ id: 'ship-test-failure-triage', stripped: 'D1 — Ship?' });
});
test('reads a signal hint and strips the whole marker', () => {
expect(parseQidMarker('Add caching? <gstack-qid:plan-ceo-review-add-caching;signal=scope-appetite>')).toEqual({
id: 'plan-ceo-review-add-caching',
signal_key: 'scope-appetite',
stripped: 'Add caching?',
});
});
test('lowercases an uppercase marker', () => {
expect(parseQidMarker('<gstack-qid:Plan-CEO-X;signal=Scope-Appetite> q')).toMatchObject({ id: 'plan-ceo-x', signal_key: 'scope-appetite' });
});
test('a malformed signal hint drops only the hint; the id still names the question', () => {
expect(parseQidMarker('Q? <gstack-qid:plan-x;signal=scope_appetite>')).toEqual({ id: 'plan-x', stripped: 'Q?' });
expect(parseQidMarker('Q? <gstack-qid:plan-x;signal=>')).toEqual({ id: 'plan-x', stripped: 'Q?' });
expect(parseQidMarker('Q? <gstack-qid:plan-x;signal=scope appetite>')).toEqual({ id: 'plan-x', stripped: 'Q?' });
expect(parseQidMarker('Q? <gstack-qid:plan-x;signl=scope-appetite>')).toEqual({ id: 'plan-x', stripped: 'Q?' });
});
test('no marker returns undefined', () => {
expect(parseQidMarker('plain question')).toBeUndefined();
});
});
describe('resolveHintedOptionKey (#3442)', () => {
test('an exact option key passes through', () => {
expect(resolveHintedOptionKey('scope-appetite', 'expand')).toBe('expand');
});
test('a label that starts with the key words maps to the key', () => {
expect(resolveHintedOptionKey('scope-appetite', 'B) Reduce scope (recommended)')).toBe('reduce');
expect(resolveHintedOptionKey('architecture-care', 'Accept risk and move on')).toBe('accept-risk');
});
test('a cue-table word never maps, so a yes cannot invert a signal', () => {
expect(resolveHintedOptionKey('scope-appetite', 'Yes, cut it')).toBeUndefined();
expect(resolveHintedOptionKey('architecture-care', 'Fix it later')).toBeUndefined();
});
test('an unknown signal key maps nothing', () => {
expect(resolveHintedOptionKey('made-up', 'expand')).toBeUndefined();
});
test('signalKeyLines lists every signal with its option keys', () => {
expect(signalKeyLines()).toContain('architecture-care: fix-now | defer | accept-risk');
});
});