|
13 | 13 | import {test} from 'node:test' |
14 | 14 | import assert from 'node:assert/strict' |
15 | 15 |
|
16 | | -import {extractSchemaFields, resolveContext, stripStringsAndComments} from './major-change-check.js' |
| 16 | +import {mkdtemp, rm, writeFile, mkdir} from 'node:fs/promises' |
| 17 | +import os from 'node:os' |
| 18 | +import * as path from 'pathe' |
| 19 | + |
| 20 | +import {checkChangesets, extractSchemaFields, resolveContext, stripStringsAndComments} from './major-change-check.js' |
17 | 21 |
|
18 | 22 | test('extracts top-level keys from a flat .object({...})', () => { |
19 | 23 | const src = ` |
@@ -185,6 +189,76 @@ test('resolveContext: git failure degrades to scanning everything against main', |
185 | 189 | assert.equal(ctx.changedFiles, null, 'git failure must NOT collapse to an empty diff set') |
186 | 190 | }) |
187 | 191 |
|
| 192 | +// --------------------------------------------------------------------------- |
| 193 | +// checkChangesets() — only flag changesets the PR actually touched |
| 194 | +// --------------------------------------------------------------------------- |
| 195 | + |
| 196 | +test('checkChangesets: ignores major changesets that were not added by this PR', async () => { |
| 197 | + // Stand up a fake repo containing two changesets on disk — one already |
| 198 | + // on main (not in the PR diff) and one introduced by this PR. Only the |
| 199 | + // latter should be reported. This is the regression for PR #7532, where |
| 200 | + // an in-flight major changeset (`thin-webs-notice.md`) on `main` was |
| 201 | + // failing the breaking-change check on every unrelated PR. |
| 202 | + const tmp = await mkdtemp(path.join(os.tmpdir(), 'changeset-scope-')) |
| 203 | + try { |
| 204 | + await mkdir(path.join(tmp, '.changeset'), {recursive: true}) |
| 205 | + await writeFile( |
| 206 | + path.join(tmp, '.changeset', 'preexisting-major.md'), |
| 207 | + `---\n'@shopify/cli': major\n---\n\nStaged for next major.\n`, |
| 208 | + ) |
| 209 | + await writeFile( |
| 210 | + path.join(tmp, '.changeset', 'pr-introduced-major.md'), |
| 211 | + `---\n'@shopify/app': major\n---\n\nIntroduced by this PR.\n`, |
| 212 | + ) |
| 213 | + |
| 214 | + const result = await checkChangesets({ |
| 215 | + cwd: tmp, |
| 216 | + changedFiles: new Set(['.changeset/pr-introduced-major.md']), |
| 217 | + }) |
| 218 | + assert.equal(result.length, 1, 'only the PR-introduced changeset is flagged') |
| 219 | + assert.equal(result[0].file, 'pr-introduced-major.md') |
| 220 | + } finally { |
| 221 | + await rm(tmp, {recursive: true, force: true}) |
| 222 | + } |
| 223 | +}) |
| 224 | + |
| 225 | +test('checkChangesets: with no changedFiles set, scans every changeset (legacy local mode)', async () => { |
| 226 | + const tmp = await mkdtemp(path.join(os.tmpdir(), 'changeset-scope-')) |
| 227 | + try { |
| 228 | + await mkdir(path.join(tmp, '.changeset'), {recursive: true}) |
| 229 | + await writeFile( |
| 230 | + path.join(tmp, '.changeset', 'a.md'), |
| 231 | + `---\n'@shopify/cli': major\n---\n`, |
| 232 | + ) |
| 233 | + await writeFile( |
| 234 | + path.join(tmp, '.changeset', 'b.md'), |
| 235 | + `---\n'@shopify/app': major\n---\n`, |
| 236 | + ) |
| 237 | + const result = await checkChangesets({cwd: tmp}) |
| 238 | + assert.equal(result.length, 2) |
| 239 | + } finally { |
| 240 | + await rm(tmp, {recursive: true, force: true}) |
| 241 | + } |
| 242 | +}) |
| 243 | + |
| 244 | +test('checkChangesets: returns empty when none of the changesets were touched by the PR', async () => { |
| 245 | + const tmp = await mkdtemp(path.join(os.tmpdir(), 'changeset-scope-')) |
| 246 | + try { |
| 247 | + await mkdir(path.join(tmp, '.changeset'), {recursive: true}) |
| 248 | + await writeFile( |
| 249 | + path.join(tmp, '.changeset', 'preexisting.md'), |
| 250 | + `---\n'@shopify/cli': major\n---\n`, |
| 251 | + ) |
| 252 | + const result = await checkChangesets({ |
| 253 | + cwd: tmp, |
| 254 | + changedFiles: new Set(['packages/app/src/foo.ts']), |
| 255 | + }) |
| 256 | + assert.equal(result.length, 0) |
| 257 | + } finally { |
| 258 | + await rm(tmp, {recursive: true, force: true}) |
| 259 | + } |
| 260 | +}) |
| 261 | + |
188 | 262 | test('resolveContext: no GITHUB_BASE_REF falls back to scanning main (local invocation)', async () => { |
189 | 263 | let called = false |
190 | 264 | const runGit = async () => { |
|
0 commit comments