-
Notifications
You must be signed in to change notification settings - Fork 354
Expand file tree
/
Copy pathchangeset.test.js
More file actions
executable file
·204 lines (172 loc) · 8.13 KB
/
changeset.test.js
File metadata and controls
executable file
·204 lines (172 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#!/usr/bin/env node
/**
* Test for Changeset Script
*
* Validates that the changeset script correctly:
* - Parses changeset files with and without codemod sections
* - Extracts codemod sections separately from descriptions
* - Consolidates multiple codemod entries
* - Excludes codemod sections from changelog entries
*/
const fs = require("fs");
const path = require("path");
const os = require("os");
const { execSync } = require("child_process");
// Create temporary directory for test changesets
const TEST_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "changeset-test-"));
const CHANGESET_DIR = path.join(TEST_DIR, ".changeset");
fs.mkdirSync(CHANGESET_DIR);
// Path to changeset script
const CHANGESET_SCRIPT = path.join(__dirname, "changeset.js");
/**
* Test helper to check if content contains expected string
*/
function assertContains(content, expected, testName) {
if (!content.includes(expected)) {
console.error(`❌ FAIL: ${testName}`);
console.error(` Expected to find: "${expected}"`);
console.error(` In content: ${content.substring(0, 200)}...`);
return false;
}
console.log(`✓ PASS: ${testName}`);
return true;
}
/**
* Test helper to check if content does NOT contain unexpected string
*/
function assertNotContains(content, unexpected, testName) {
if (content.includes(unexpected)) {
console.error(`❌ FAIL: ${testName}`);
console.error(` Expected NOT to find: "${unexpected}"`);
return false;
}
console.log(`✓ PASS: ${testName}`);
return true;
}
/**
* Create a test changeset file
*/
function createChangeset(filename, bumpType, description, codemod = null) {
let content = `---
"gh-aw": ${bumpType}
---
${description}`;
if (codemod) {
content += `\n\n## Codemod\n\n${codemod}`;
}
fs.writeFileSync(path.join(CHANGESET_DIR, filename), content, "utf8");
}
/**
* Run changeset version command and capture output
*/
function runChangesetVersion() {
try {
// Set up git for testing
execSync("git init", { cwd: TEST_DIR, stdio: "ignore" });
execSync('git config user.email "test@example.com"', { cwd: TEST_DIR, stdio: "ignore" });
execSync('git config user.name "Test User"', { cwd: TEST_DIR, stdio: "ignore" });
// Run version command by requiring and calling main
// Need to set up process.argv properly: [node, scriptname, command, ...]
const output = execSync(`node -e "process.argv.splice(1, 0, 'changeset.js', 'version'); const {main} = require('${CHANGESET_SCRIPT}'); main().catch(console.error);"`, {
cwd: TEST_DIR,
encoding: "utf8",
env: { ...process.env, GH_AW_CURRENT_VERSION: "v0.1.0" },
});
return output;
} catch (error) {
return error.stdout || error.message;
}
}
/**
* Clean up test directory
*/
function cleanup() {
try {
fs.rmSync(TEST_DIR, { recursive: true, force: true });
} catch (error) {
console.error("Cleanup failed:", error.message);
}
}
// Run tests
function runTests() {
let allPassed = true;
try {
// Test 1: Changeset without codemod
console.log("\n=== Test 1: Changeset without codemod ===");
createChangeset("patch-fix.md", "patch", "Fixed a bug in the rendering logic");
let output = runChangesetVersion();
allPassed &= assertContains(output, "Fixed a bug in the rendering logic", "Should include description in changes list");
allPassed &= assertContains(output, "Would add to CHANGELOG.md", "Should show changelog preview");
allPassed &= assertNotContains(output, "Consolidated Codemod Instructions", "Should not show codemod section when none exist");
// Clean up for next test
fs.unlinkSync(path.join(CHANGESET_DIR, "patch-fix.md"));
// Test 2: Single changeset with codemod
console.log("\n=== Test 2: Single changeset with codemod ===");
createChangeset(
"minor-breaking.md",
"minor",
"Changed the workflow frontmatter field `engine` to require an object instead of a string.",
"If you have workflows using `engine: copilot`, update to:\n\n```yaml\nengine:\n id: copilot\n```"
);
output = runChangesetVersion();
allPassed &= assertContains(output, "Consolidated Codemod Instructions", "Should show codemod section");
allPassed &= assertContains(output, "The following breaking changes require code updates", "Should include codemod header");
allPassed &= assertContains(output, "engine:\n id: copilot", "Should include codemod content");
allPassed &= assertContains(output, "### Changed the workflow frontmatter field", "Should include description as heading in codemod");
// Verify codemod is NOT in raw form in the changelog section, but IS in the Migration Guide
const changelogSection = output.substring(output.indexOf("Would add to CHANGELOG.md"), output.indexOf("Consolidated Codemod"));
allPassed &= assertNotContains(changelogSection, "## Codemod", 'Changelog should not contain "## Codemod" heading');
allPassed &= assertContains(changelogSection, "### Migration Guide", "Changelog should contain Migration Guide section");
allPassed &= assertContains(changelogSection, "`````markdown", "Changelog should contain markdown code block with 5 backticks for codemods");
// Clean up for next test
fs.unlinkSync(path.join(CHANGESET_DIR, "minor-breaking.md"));
// Test 3: Multiple changesets with codemods
console.log("\n=== Test 3: Multiple changesets with codemods ===");
createChangeset("minor-engine.md", "minor", "Changed engine configuration format", "Update `engine: copilot` to `engine: { id: copilot }`");
createChangeset("minor-tools.md", "minor", "Changed tools configuration format", "Update `tools: github` to `tools: [github]`");
output = runChangesetVersion();
allPassed &= assertContains(output, "Consolidated Codemod Instructions", "Should show consolidated codemod section");
allPassed &= assertContains(output, "Changed engine configuration format", "Should include first codemod");
allPassed &= assertContains(output, "Changed tools configuration format", "Should include second codemod");
allPassed &= assertContains(output, "Update `engine: copilot`", "Should include first codemod content");
allPassed &= assertContains(output, "Update `tools: github`", "Should include second codemod content");
// Clean up for next test
fs.unlinkSync(path.join(CHANGESET_DIR, "minor-engine.md"));
fs.unlinkSync(path.join(CHANGESET_DIR, "minor-tools.md"));
// Test 4: Mixed changesets (with and without codemods)
console.log("\n=== Test 4: Mixed changesets with and without codemods ===");
createChangeset("patch-fix.md", "patch", "Fixed a bug");
createChangeset("minor-breaking.md", "minor", "Breaking change", "Update your code like this");
output = runChangesetVersion();
allPassed &= assertContains(output, "Fixed a bug", "Should include non-codemod change");
allPassed &= assertContains(output, "Breaking change", "Should include codemod change");
allPassed &= assertContains(output, "Consolidated Codemod Instructions", "Should show codemod section");
allPassed &= assertContains(output, "Update your code like this", "Should include codemod content");
// Clean up for next test
fs.unlinkSync(path.join(CHANGESET_DIR, "patch-fix.md"));
fs.unlinkSync(path.join(CHANGESET_DIR, "minor-breaking.md"));
// Test 5: No changesets with version command
console.log("\n=== Test 5: No changesets with version command ===");
output = runChangesetVersion();
allPassed &= assertContains(output, "No changesets found", "Should show message when no changesets exist");
// Test 6: No changesets with explicit release type (simulate updateChangelog)
console.log("\n=== Test 6: CHANGELOG entry for release without changesets ===");
// The actual changelog update behavior will be tested manually
console.log("\n=== Test Results ===");
if (allPassed) {
console.log("✓ All tests passed!");
return 0;
} else {
console.log("❌ Some tests failed");
return 1;
}
} catch (error) {
console.error("Test execution failed:", error.message);
return 1;
} finally {
cleanup();
}
}
// Run tests and exit with appropriate code
const exitCode = runTests();
process.exit(exitCode);