Skip to content

Commit 24ffc9f

Browse files
Copilotvaind
andcommitted
Add comprehensive tests for legal boilerplate validation
Co-authored-by: vaind <6349682+vaind@users.noreply.github.com>
1 parent 294196e commit 24ffc9f

3 files changed

Lines changed: 288 additions & 36 deletions

File tree

Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
const { describe, it } = require('node:test');
2+
const assert = require('node:assert');
3+
const { extractLegalBoilerplateSection } = require('./dangerfile-utils.js');
4+
5+
describe('Legal Boilerplate Validation', () => {
6+
describe('extractLegalBoilerplateSection', () => {
7+
it('should extract legal boilerplate section with ### header', () => {
8+
const template = `
9+
# Pull Request Template
10+
11+
## Description
12+
Please describe your changes
13+
14+
### Legal Boilerplate
15+
Look, I get it. The entity doing business as "Sentry" was incorporated in the State of Delaware in 2015 as Functional Software, Inc. and is gonna need some rights from me in order to utilize my contributions in this here PR. So here's the deal: I retain all rights, title and interest in and to my contributions, and by keeping this boilerplate intact I confirm that Sentry can use, modify, copy, and redistribute my contributions, under Sentry's choice of terms.
16+
17+
## Checklist
18+
- [ ] Tests added
19+
`;
20+
21+
const result = extractLegalBoilerplateSection(template);
22+
23+
assert.ok(result.includes('### Legal Boilerplate'), 'Should include the header');
24+
assert.ok(result.includes('Functional Software, Inc.'), 'Should include the legal text');
25+
assert.ok(!result.includes('## Checklist'), 'Should not include the next section');
26+
});
27+
28+
it('should extract legal boilerplate section with ## header', () => {
29+
const template = `
30+
# Pull Request Template
31+
32+
## Legal Boilerplate
33+
34+
This is a legal notice.
35+
36+
## Other Section
37+
More content
38+
`;
39+
40+
const result = extractLegalBoilerplateSection(template);
41+
42+
assert.strictEqual(result.trim(), '## Legal Boilerplate\n\nThis is a legal notice.');
43+
});
44+
45+
it('should extract legal boilerplate section with different heading levels', () => {
46+
const testCases = [
47+
{ header: '# Legal Boilerplate', text: 'Level 1 header' },
48+
{ header: '## Legal Boilerplate', text: 'Level 2 header' },
49+
{ header: '### Legal Boilerplate', text: 'Level 3 header' },
50+
{ header: '#### Legal Boilerplate', text: 'Level 4 header' },
51+
{ header: '##### Legal Boilerplate', text: 'Level 5 header' },
52+
{ header: '###### Legal Boilerplate', text: 'Level 6 header' }
53+
];
54+
55+
testCases.forEach(({ header, text }) => {
56+
const template = `${header}\n${text}\n## Next Section`;
57+
const result = extractLegalBoilerplateSection(template);
58+
59+
assert.ok(result.includes(header), `Should extract section with ${header}`);
60+
assert.ok(result.includes(text), `Should include text for ${header}`);
61+
assert.ok(!result.includes('Next Section'), `Should not include next section for ${header}`);
62+
});
63+
});
64+
65+
it('should handle case-insensitive matching', () => {
66+
const templates = [
67+
'### Legal Boilerplate\nContent',
68+
'### legal boilerplate\nContent',
69+
'### LEGAL BOILERPLATE\nContent',
70+
'### Legal BOILERPLATE\nContent'
71+
];
72+
73+
templates.forEach(template => {
74+
const result = extractLegalBoilerplateSection(template);
75+
assert.ok(result.length > 0, `Should extract from: ${template.split('\n')[0]}`);
76+
assert.ok(result.includes('Content'), `Should include content from: ${template.split('\n')[0]}`);
77+
});
78+
});
79+
80+
it('should handle legal boilerplate with multiple paragraphs', () => {
81+
const template = `
82+
### Legal Boilerplate
83+
84+
First paragraph of legal text.
85+
86+
Second paragraph of legal text.
87+
88+
Third paragraph of legal text.
89+
90+
## Next Section
91+
`;
92+
93+
const result = extractLegalBoilerplateSection(template);
94+
95+
assert.ok(result.includes('First paragraph'), 'Should include first paragraph');
96+
assert.ok(result.includes('Second paragraph'), 'Should include second paragraph');
97+
assert.ok(result.includes('Third paragraph'), 'Should include third paragraph');
98+
assert.ok(!result.includes('Next Section'), 'Should not include next section');
99+
});
100+
101+
it('should handle legal boilerplate at end of template', () => {
102+
const template = `
103+
# PR Template
104+
105+
## Description
106+
Content
107+
108+
### Legal Boilerplate
109+
Legal text at the end.
110+
`;
111+
112+
const result = extractLegalBoilerplateSection(template);
113+
114+
assert.ok(result.includes('### Legal Boilerplate'), 'Should include header');
115+
assert.ok(result.includes('Legal text at the end.'), 'Should include text');
116+
});
117+
118+
it('should return empty string when no legal boilerplate section exists', () => {
119+
const template = `
120+
# Pull Request Template
121+
122+
## Description
123+
Please describe your changes
124+
125+
## Checklist
126+
- [ ] Tests added
127+
`;
128+
129+
const result = extractLegalBoilerplateSection(template);
130+
131+
assert.strictEqual(result, '', 'Should return empty string when no legal section found');
132+
});
133+
134+
it('should handle empty template', () => {
135+
const result = extractLegalBoilerplateSection('');
136+
assert.strictEqual(result, '', 'Should return empty string for empty template');
137+
});
138+
139+
it('should handle template with only legal boilerplate section', () => {
140+
const template = '### Legal Boilerplate\nThis is the only content.';
141+
const result = extractLegalBoilerplateSection(template);
142+
143+
assert.ok(result.includes('### Legal Boilerplate'), 'Should include header');
144+
assert.ok(result.includes('This is the only content.'), 'Should include content');
145+
});
146+
147+
it('should handle legal boilerplate with special characters', () => {
148+
const template = `
149+
### Legal Boilerplate
150+
Text with special chars: @#$%^&*()_+-={}[]|\\:";'<>?,./
151+
And some unicode: 你好世界 🎉
152+
153+
## Next
154+
`;
155+
156+
const result = extractLegalBoilerplateSection(template);
157+
158+
assert.ok(result.includes('special chars'), 'Should handle special characters');
159+
assert.ok(result.includes('你好世界'), 'Should handle unicode');
160+
assert.ok(result.includes('🎉'), 'Should handle emoji');
161+
});
162+
163+
it('should handle legal boilerplate with code blocks', () => {
164+
const template = `
165+
### Legal Boilerplate
166+
167+
Some text with code:
168+
169+
\`\`\`javascript
170+
const legal = true;
171+
\`\`\`
172+
173+
More text.
174+
175+
## Next Section
176+
`;
177+
178+
const result = extractLegalBoilerplateSection(template);
179+
180+
assert.ok(result.includes('const legal = true;'), 'Should include code blocks');
181+
assert.ok(result.includes('More text.'), 'Should include text after code block');
182+
assert.ok(!result.includes('Next Section'), 'Should not include next section');
183+
});
184+
185+
it('should handle legal boilerplate with lists', () => {
186+
const template = `
187+
### Legal Boilerplate
188+
189+
You agree to:
190+
- Item 1
191+
- Item 2
192+
- Item 3
193+
194+
## Other
195+
`;
196+
197+
const result = extractLegalBoilerplateSection(template);
198+
199+
assert.ok(result.includes('- Item 1'), 'Should include list items');
200+
assert.ok(result.includes('- Item 2'), 'Should include list items');
201+
assert.ok(result.includes('- Item 3'), 'Should include list items');
202+
});
203+
204+
it('should handle legal boilerplate with extra whitespace', () => {
205+
const template = `
206+
### Legal Boilerplate
207+
Content with spaces.
208+
## Next
209+
`;
210+
211+
const result = extractLegalBoilerplateSection(template);
212+
213+
assert.ok(result.includes('Content with spaces.'), 'Should handle extra whitespace in header');
214+
});
215+
216+
it('should stop at first subsequent header', () => {
217+
const template = `
218+
### Legal Boilerplate
219+
First section content.
220+
### Another Legal Boilerplate
221+
This should not be included.
222+
`;
223+
224+
const result = extractLegalBoilerplateSection(template);
225+
226+
assert.ok(result.includes('First section content.'), 'Should include first section');
227+
assert.ok(!result.includes('This should not be included.'), 'Should stop at next header');
228+
});
229+
230+
it('should handle blank lines within legal section', () => {
231+
const template = `
232+
### Legal Boilerplate
233+
234+
First paragraph.
235+
236+
237+
Second paragraph with blank lines above.
238+
239+
## Next
240+
`;
241+
242+
const result = extractLegalBoilerplateSection(template);
243+
244+
assert.ok(result.includes('First paragraph.'), 'Should include first paragraph');
245+
assert.ok(result.includes('Second paragraph'), 'Should include second paragraph');
246+
// Should preserve blank lines
247+
const blankLineCount = (result.match(/\n\n/g) || []).length;
248+
assert.ok(blankLineCount >= 1, 'Should preserve blank lines');
249+
});
250+
});
251+
});

danger/dangerfile-utils.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,43 @@ function extractPRFlavor(prTitle, prBranchRef) {
8686
return "";
8787
}
8888

89+
/**
90+
* Extract the legal boilerplate section from the PR template
91+
* @param {string} templateContent - The PR template content
92+
* @returns {string} The extracted legal boilerplate section
93+
*/
94+
function extractLegalBoilerplateSection(templateContent) {
95+
// Find the legal boilerplate section and extract it
96+
const lines = templateContent.split('\n');
97+
let inLegalSection = false;
98+
let legalSection = [];
99+
100+
for (let i = 0; i < lines.length; i++) {
101+
const line = lines[i];
102+
103+
// Check if this line is the legal boilerplate header
104+
if (/^#{1,6}\s+Legal\s+Boilerplate/i.test(line)) {
105+
inLegalSection = true;
106+
legalSection.push(line);
107+
continue;
108+
}
109+
110+
// If we're in the legal section
111+
if (inLegalSection) {
112+
// Check if we've reached another header (end of legal section)
113+
if (/^#{1,6}\s+/.test(line)) {
114+
break;
115+
}
116+
legalSection.push(line);
117+
}
118+
}
119+
120+
return legalSection.join('\n').trim();
121+
}
122+
89123
module.exports = {
90124
FLAVOR_CONFIG,
91125
getFlavorConfig,
92-
extractPRFlavor
126+
extractPRFlavor,
127+
extractLegalBoilerplateSection
93128
};

danger/dangerfile.js

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const { getFlavorConfig, extractPRFlavor } = require('./dangerfile-utils.js');
1+
const { getFlavorConfig, extractPRFlavor, extractLegalBoilerplateSection } = require('./dangerfile-utils.js');
22

33
const headRepoName = danger.github.pr.head.repo.git_url;
44
const baseRepoName = danger.github.pr.base.repo.git_url;
@@ -289,40 +289,6 @@ This is required to ensure proper intellectual property rights for your contribu
289289
console.log('::debug:: Legal boilerplate found in PR description ✓');
290290
}
291291

292-
/**
293-
* Extract the legal boilerplate section from the PR template
294-
* @param {string} templateContent - The PR template content
295-
* @returns {string} The extracted legal boilerplate section
296-
*/
297-
function extractLegalBoilerplateSection(templateContent) {
298-
// Find the legal boilerplate section and extract it
299-
const lines = templateContent.split('\n');
300-
let inLegalSection = false;
301-
let legalSection = [];
302-
303-
for (let i = 0; i < lines.length; i++) {
304-
const line = lines[i];
305-
306-
// Check if this line is the legal boilerplate header
307-
if (/^#{1,6}\s+Legal\s+Boilerplate/i.test(line)) {
308-
inLegalSection = true;
309-
legalSection.push(line);
310-
continue;
311-
}
312-
313-
// If we're in the legal section
314-
if (inLegalSection) {
315-
// Check if we've reached another header (end of legal section)
316-
if (/^#{1,6}\s+/.test(line)) {
317-
break;
318-
}
319-
legalSection.push(line);
320-
}
321-
}
322-
323-
return legalSection.join('\n').trim();
324-
}
325-
326292
async function checkFromExternalChecks() {
327293
// Get the external dangerfile path from environment variable (passed via workflow input)
328294
// Priority: EXTRA_DANGERFILE (absolute path) -> EXTRA_DANGERFILE_INPUT (relative path)

0 commit comments

Comments
 (0)