mirror of
https://github.com/bherbruck/solidcraft.git
synced 2026-11-06 03:51:08 +00:00
15e7615ce3
A pull request now starts by saying what it is in plain words, and shows anything there is to look at in its description. A file in the diff doesn't count. The new Picture check fails a PR that adds to the showcase or docs/screenshots without showing a picture. It isn't a required check. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
197 lines
9.3 KiB
JavaScript
197 lines
9.3 KiB
JavaScript
'use strict';
|
||
const test = require('node:test');
|
||
const assert = require('node:assert/strict');
|
||
const pc = require('./pr-checks.js');
|
||
|
||
const REPO = 'tangency-labs/solidcraft';
|
||
|
||
test('parseClosing finds closing keywords only', () => {
|
||
assert.deepEqual(pc.parseClosing('Fixes #12', REPO), [12]);
|
||
assert.deepEqual(pc.parseClosing('closes: #3\nResolved #4', REPO), [3, 4]);
|
||
assert.deepEqual(pc.parseClosing('Fixes #5, #6 and #7', REPO), [5, 6, 7]);
|
||
assert.deepEqual(pc.parseClosing('Fix #8', REPO), [8]);
|
||
assert.deepEqual(pc.parseClosing('Refs #9\nSee #10\nrelated to #11', REPO), []);
|
||
assert.deepEqual(pc.parseClosing('Prefixes #1 and suffix fixes#2', REPO), []);
|
||
assert.deepEqual(pc.parseClosing(null, REPO), []);
|
||
});
|
||
|
||
test('parseClosing handles repo-qualified refs, comments and code', () => {
|
||
assert.deepEqual(pc.parseClosing(`Fixes ${REPO}#20`, REPO), [20]);
|
||
assert.deepEqual(pc.parseClosing('Fixes other/repo#21', REPO), []);
|
||
assert.deepEqual(pc.parseClosing('<!-- Fixes #22 -->', REPO), []);
|
||
assert.deepEqual(pc.parseClosing('```\nFixes #23\n```\nFixes #24', REPO), [24]);
|
||
assert.deepEqual(pc.parseClosing('say `Fixes #25` to close', REPO), []);
|
||
});
|
||
|
||
test('parseDefaults', () => {
|
||
assert.equal(pc.parseDefaults('x').status, 'missing');
|
||
assert.equal(pc.parseDefaults('<!-- Changes a default: no -->').status, 'missing');
|
||
assert.equal(pc.parseDefaults('Changes a default: no').status, 'no');
|
||
assert.equal(pc.parseDefaults('- Changes a default: No.\r\nmore').status, 'no');
|
||
assert.equal(pc.parseDefaults('**Changes a default:** no').status, 'no');
|
||
assert.equal(pc.parseDefaults('Changes a default: <no | yes — <what>, old behaviour kept behind preference <name>>').status, 'malformed');
|
||
assert.equal(pc.parseDefaults('Changes a default:').status, 'malformed');
|
||
assert.equal(pc.parseDefaults('Changes a default: maybe').status, 'malformed');
|
||
assert.equal(pc.parseDefaults('Changes a default: yes').status, 'malformed');
|
||
assert.equal(pc.parseDefaults('Changes a default: yes - faster zoom').status, 'malformed');
|
||
});
|
||
|
||
test('parseDefaults yes forms', () => {
|
||
for (const sep of ['—', '–', '-', '->', '→', ':']) {
|
||
const r = pc.parseDefaults(`Changes a default: yes ${sep} zoom speed doubled, old behaviour kept behind preference viewport.zoom_speed`);
|
||
assert.equal(r.status, 'yes', sep);
|
||
assert.equal(r.preference, 'viewport.zoom_speed');
|
||
assert.equal(r.detail, 'zoom speed doubled');
|
||
}
|
||
assert.equal(pc.parseDefaults('Changes a default: yes — x, old value kept behind preference `p`').status, 'yes');
|
||
assert.equal(pc.parseDefaults('Changes a default: yes — x, old behaviour kept behind preference <name>').status, 'malformed');
|
||
});
|
||
|
||
test('isBot and isDocsOnly', () => {
|
||
assert.ok(pc.isBot({ login: 'dependabot[bot]', type: 'Bot' }));
|
||
assert.ok(pc.isBot({ login: 'solidcraft-generated[bot]' }));
|
||
assert.ok(!pc.isBot({ login: 'alice', type: 'User' }));
|
||
assert.ok(pc.isDocsOnly(['README.md', 'docs/a.png']));
|
||
assert.ok(!pc.isDocsOnly(['README.md', 'crates/a.rs']));
|
||
assert.ok(!pc.isDocsOnly([]));
|
||
});
|
||
|
||
const mk = (number, login, body, extra = {}) => ({
|
||
number, state: 'open', user: { login }, body, title: `t${number}`,
|
||
html_url: `https://github.com/${REPO}/pull/${number}`, labels: [], ...extra,
|
||
});
|
||
|
||
test('findDuplicates: lower number, other author, same issue', () => {
|
||
const pulls = [
|
||
mk(10, 'bob', 'Fixes #5'),
|
||
mk(11, 'carol', 'Fixes #6'),
|
||
mk(12, 'alice', 'Fixes #5'),
|
||
mk(13, 'dan', 'Refs #5'),
|
||
mk(14, 'erin', 'Closes #5', { draft: true }),
|
||
mk(15, 'fay', 'Fixes #5', { labels: [{ name: 'not-a-duplicate' }] }),
|
||
mk(30, 'gus', 'Fixes #5'),
|
||
mk(16, 'hal', 'Fixes #5', { state: 'closed' }),
|
||
];
|
||
const d = pc.findDuplicates({ number: 20, author: 'alice', issues: [5], pulls, repoFullName: REPO });
|
||
assert.deepEqual(d.map((x) => x.number), [10, 14]);
|
||
assert.ok(d[1].draft);
|
||
});
|
||
|
||
function fakes(pull, { pulls = [], files = [] } = {}) {
|
||
const calls = [];
|
||
const github = {
|
||
paginate: async (fn) => (fn === 'list' ? pulls : files),
|
||
rest: {
|
||
pulls: { list: 'list', listFiles: 'files' },
|
||
issues: {
|
||
addLabels: async (a) => calls.push(['add', a.labels]),
|
||
removeLabel: async (a) => calls.push(['remove', a.name]),
|
||
},
|
||
},
|
||
};
|
||
const failures = [];
|
||
const core = { setFailed: (m) => failures.push(m), info() {}, notice() {} };
|
||
const context = { repo: { owner: 'tangency-labs', repo: 'solidcraft' }, payload: { pull_request: pull } };
|
||
return { github, core, context, calls, failures };
|
||
}
|
||
|
||
test('checkDuplicate fails with links, skips on label and bots', async () => {
|
||
const me = mk(20, 'alice', 'Fixes #5');
|
||
let f = fakes(me, { pulls: [mk(10, 'bob', 'Fixes #5')] });
|
||
await pc.checkDuplicate(f);
|
||
assert.equal(f.failures.length, 1);
|
||
assert.match(f.failures[0], /pull\/10/);
|
||
|
||
f = fakes({ ...me, labels: [{ name: 'not-a-duplicate' }] }, { pulls: [mk(10, 'bob', 'Fixes #5')] });
|
||
await pc.checkDuplicate(f);
|
||
assert.equal(f.failures.length, 0);
|
||
|
||
f = fakes({ ...me, user: { login: 'dependabot[bot]', type: 'Bot' } }, { pulls: [mk(10, 'bob', 'Fixes #5')] });
|
||
await pc.checkDuplicate(f);
|
||
assert.equal(f.failures.length, 0);
|
||
});
|
||
|
||
test('checkDefaults fails when missing, passes docs-only and bots', async () => {
|
||
const me = mk(20, 'alice', 'Fixes #5', { created_at: '2026-10-25T00:00:00Z' });
|
||
let f = fakes(me, { files: [{ filename: 'crates/a.rs' }] });
|
||
await pc.checkDefaults(f);
|
||
assert.equal(f.failures.length, 1);
|
||
|
||
f = fakes(me, { files: [{ filename: 'docs/a.md' }] });
|
||
await pc.checkDefaults(f);
|
||
assert.equal(f.failures.length, 0);
|
||
|
||
f = fakes({ ...me, body: 'Changes a default: no' }, { files: [{ filename: 'crates/a.rs' }] });
|
||
await pc.checkDefaults(f);
|
||
assert.equal(f.failures.length, 0);
|
||
});
|
||
|
||
test('syncDefaultLabel adds and removes', async () => {
|
||
const yes = 'Changes a default: yes — a, old behaviour kept behind preference p';
|
||
let f = fakes(mk(1, 'a', yes));
|
||
await pc.syncDefaultLabel(f);
|
||
assert.deepEqual(f.calls, [['add', ['changes-default']]]);
|
||
f = fakes(mk(1, 'a', 'Changes a default: no', { labels: [{ name: 'changes-default' }] }));
|
||
await pc.syncDefaultLabel(f);
|
||
assert.deepEqual(f.calls, [['remove', 'changes-default']]);
|
||
});
|
||
|
||
test('syncDefaultLabel notes a read-only token instead of failing', async () => {
|
||
const yes = 'Changes a default: yes — a, old behaviour kept behind preference p';
|
||
const f = fakes(mk(1, 'a', yes));
|
||
const notes = [];
|
||
f.core.notice = (m) => notes.push(m);
|
||
f.github.rest.issues.addLabels = async () => { throw Object.assign(new Error('Resource not accessible by integration'), { status: 403 }); };
|
||
await pc.syncDefaultLabel(f);
|
||
assert.equal(notes.length, 1);
|
||
f.github.rest.issues.addLabels = async () => { throw Object.assign(new Error('server error'), { status: 500 }); };
|
||
await assert.rejects(pc.syncDefaultLabel(f));
|
||
});
|
||
|
||
test('checkDefaults only enforces from 2026-10-25', async () => {
|
||
const files = [{ filename: 'crates/a.rs' }];
|
||
const old = mk(20, 'alice', 'Fixes #5', { created_at: '2026-10-24T23:59:59Z' });
|
||
let f = fakes(old, { files });
|
||
assert.deepEqual(await pc.checkDefaults(f), { skipped: 'opened before enforcement' });
|
||
assert.equal(f.failures.length, 0);
|
||
f = fakes({ ...old, created_at: '2026-10-25T00:00:00Z' }, { files });
|
||
await pc.checkDefaults(f);
|
||
assert.equal(f.failures.length, 1);
|
||
});
|
||
|
||
test('hasPicture needs one a reader can see in the description', () => {
|
||
assert.equal(pc.hasPicture(''), true);
|
||
assert.equal(pc.hasPicture('<img width="960" alt="x" src="https://github.com/user-attachments/assets/abc" />'), true);
|
||
assert.equal(pc.hasPicture('<video src="https://example.com/a.mp4"></video>'), true);
|
||
assert.equal(pc.hasPicture('https://github.com/user-attachments/assets/abc'), true);
|
||
assert.equal(pc.hasPicture(''), false);
|
||
assert.equal(pc.hasPicture(null), false);
|
||
assert.equal(pc.hasPicture('The GIF is the change.'), false);
|
||
assert.equal(pc.hasPicture('See docs/screenshots/box.gif'), false);
|
||
assert.equal(pc.hasPicture('<!--  -->'), false);
|
||
assert.equal(pc.hasPicture('```\n\n```'), false);
|
||
assert.equal(pc.hasPicture('``'), false);
|
||
});
|
||
|
||
test('needsPicture covers the showcase and its media only', () => {
|
||
assert.equal(pc.needsPicture(['docs/showcase.md']), true);
|
||
assert.equal(pc.needsPicture(['crates/a.rs', 'docs/screenshots/a.gif']), true);
|
||
assert.equal(pc.needsPicture(['docs/mcp.md', 'crates/a.rs']), false);
|
||
});
|
||
|
||
test('checkPicture fails a showcase PR with no picture, from 2026-11-02 on', async () => {
|
||
const files = [{ filename: 'docs/showcase.md' }];
|
||
const pr = mk(30, 'alice', 'The GIF is the change.', { created_at: '2026-11-02T00:00:00Z' });
|
||
let f = fakes(pr, { files });
|
||
assert.deepEqual(await pc.checkPicture(f), { shown: false });
|
||
assert.equal(f.failures.length, 1);
|
||
f = fakes({ ...pr, body: '' }, { files });
|
||
assert.deepEqual(await pc.checkPicture(f), { shown: true });
|
||
assert.equal(f.failures.length, 0);
|
||
f = fakes({ ...pr, created_at: '2026-11-01T23:59:59Z' }, { files });
|
||
assert.deepEqual(await pc.checkPicture(f), { skipped: 'opened before enforcement' });
|
||
f = fakes(pr, { files: [{ filename: 'crates/a.rs' }] });
|
||
assert.deepEqual(await pc.checkPicture(f), { skipped: 'nothing to show' });
|
||
assert.equal(f.failures.length, 0);
|
||
});
|