Skip to content

Commit 0147e44

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
add missing files
1 parent c8f0488 commit 0147e44

File tree

3 files changed

+173
-0
lines changed

3 files changed

+173
-0
lines changed

internal/server/static/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Static Assets Testing
2+
3+
This directory contains static assets for the prcost web UI, including JavaScript functions that are tested separately.
4+
5+
## JavaScript Testing
6+
7+
Key functions are extracted into separate `.js` files for testing purposes:
8+
9+
- `formatR2RCallout.js` - Renders the Ready-to-Review savings callout
10+
- `formatR2RCallout.test.js` - Tests for the callout rendering
11+
12+
### Running Tests
13+
14+
```bash
15+
# Run JavaScript tests only
16+
make test-js
17+
18+
# Run all tests (Go + JavaScript)
19+
make test
20+
```
21+
22+
### Test Coverage
23+
24+
The JavaScript tests verify:
25+
- Correct rendering of the savings callout HTML
26+
- Proper formatting of dollar amounts ($50K, $2.5M, etc.)
27+
- Presence of key messaging ("Pro-Tip:", "Ready-to-Review", etc.)
28+
- Correct behavior for fast PRs (no callout for ≤1 hour)
29+
- HTML structure and styling
30+
31+
### Adding New Tests
32+
33+
When modifying `index.html` JavaScript functions:
34+
35+
1. Extract the function to a separate `.js` file (if not already extracted)
36+
2. Add tests to the corresponding `.test.js` file
37+
3. Run `make test-js` to verify
38+
4. Commit both the function and test files together
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Extracted from index.html for testing purposes
2+
function formatR2RCallout(avgOpenHours, r2rSavings, currentEfficiency, modeledEfficiency, targetMergeHours = 1.5) {
3+
// Only show if average merge velocity is > target
4+
if (avgOpenHours <= targetMergeHours) {
5+
return '';
6+
}
7+
8+
// Format savings with appropriate precision
9+
let savingsText;
10+
if (r2rSavings >= 1000000) {
11+
savingsText = '$' + (r2rSavings / 1000000).toFixed(1) + 'M';
12+
} else if (r2rSavings >= 1000) {
13+
savingsText = '$' + (r2rSavings / 1000).toFixed(0) + 'K';
14+
} else {
15+
savingsText = '$' + r2rSavings.toFixed(0);
16+
}
17+
18+
const efficiencyDelta = modeledEfficiency - currentEfficiency;
19+
let throughputText = '';
20+
if (efficiencyDelta > 0) {
21+
throughputText = ' (+' + efficiencyDelta.toFixed(1) + '% throughput)';
22+
}
23+
24+
// Format target merge time
25+
let targetText = targetMergeHours.toFixed(1) + 'h';
26+
27+
let html = '<div style="margin: 24px 0; padding: 12px 20px; background: linear-gradient(135deg, #e6f9f0 0%, #ffffff 100%); border: 1px solid #00c853; border-radius: 8px; font-size: 14px; color: #1d1d1f; line-height: 1.6;">';
28+
html += 'Pro-Tip: Save <strong>' + savingsText + '/yr</strong> in lost development effort by reducing merge times to &lt;' + targetText + ' with ';
29+
html += '<a href="https://codegroove.dev/" target="_blank" rel="noopener" style="color: #00c853; font-weight: 600; text-decoration: none;">Ready-to-Review</a>. ';
30+
html += 'Free for OSS, cheap for everyone else.';
31+
html += '</div>';
32+
return html;
33+
}
34+
35+
// Export for testing (Node.js) or use globally (browser)
36+
if (typeof module !== 'undefined' && module.exports) {
37+
module.exports = { formatR2RCallout };
38+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Simple test for formatR2RCallout function
2+
// Run with: node formatR2RCallout.test.js
3+
4+
const { formatR2RCallout } = require('./formatR2RCallout.js');
5+
const assert = require('assert');
6+
7+
function test(description, fn) {
8+
try {
9+
fn();
10+
console.log('✓', description);
11+
} catch (err) {
12+
console.error('✗', description);
13+
console.error(' ', err.message);
14+
process.exit(1);
15+
}
16+
}
17+
18+
// Test 1: Should return empty string for fast PRs (≤1.5 hours by default)
19+
test('Returns empty for PRs with avgOpenHours <= 1.5 (default)', () => {
20+
const result = formatR2RCallout(0.5, 50000, 60, 70);
21+
assert.strictEqual(result, '');
22+
});
23+
24+
test('Returns empty for PRs with avgOpenHours = 1.5 (default)', () => {
25+
const result = formatR2RCallout(1.5, 50000, 60, 70);
26+
assert.strictEqual(result, '');
27+
});
28+
29+
// Test 2: Should render callout for slow PRs (>1.5 hours by default)
30+
test('Renders callout for PRs with avgOpenHours > 1.5 (default)', () => {
31+
const result = formatR2RCallout(10, 50000, 60, 70);
32+
assert(result.length > 0, 'Should return non-empty HTML');
33+
});
34+
35+
// Test 3: Should contain "Pro-Tip:" text
36+
test('Contains "Pro-Tip:" text', () => {
37+
const result = formatR2RCallout(10, 50000, 60, 70);
38+
assert(result.includes('Pro-Tip:'), 'Should contain "Pro-Tip:"');
39+
});
40+
41+
// Test 4: Should contain "Ready-to-Review" link
42+
test('Contains "Ready-to-Review" link', () => {
43+
const result = formatR2RCallout(10, 50000, 60, 70);
44+
assert(result.includes('Ready-to-Review'), 'Should contain "Ready-to-Review"');
45+
assert(result.includes('href="https://codegroove.dev/"'), 'Should link to codegroove.dev');
46+
});
47+
48+
// Test 5: Should contain OSS pricing message
49+
test('Contains OSS pricing message', () => {
50+
const result = formatR2RCallout(10, 50000, 60, 70);
51+
assert(result.includes('Free for OSS, cheap for everyone else'), 'Should contain OSS pricing message');
52+
});
53+
54+
// Test 6: Should format savings in thousands (K)
55+
test('Formats savings with K suffix for thousands', () => {
56+
const result = formatR2RCallout(10, 50000, 60, 70);
57+
assert(result.includes('$50K/yr'), 'Should format $50,000 as $50K/yr');
58+
});
59+
60+
// Test 7: Should format savings in millions (M)
61+
test('Formats savings with M suffix for millions', () => {
62+
const result = formatR2RCallout(10, 2500000, 60, 70);
63+
assert(result.includes('$2.5M/yr'), 'Should format $2,500,000 as $2.5M/yr');
64+
});
65+
66+
// Test 8: Should format small savings without suffix
67+
test('Formats small savings without suffix', () => {
68+
const result = formatR2RCallout(10, 500, 60, 70);
69+
assert(result.includes('$500/yr'), 'Should format $500 as $500/yr');
70+
});
71+
72+
// Test 9: Should contain "reducing merge times to <1.5h" (default)
73+
test('Contains merge time reduction message (default 1.5h)', () => {
74+
const result = formatR2RCallout(10, 50000, 60, 70);
75+
assert(result.includes('reducing merge times to &lt;1.5h'), 'Should mention reducing merge times to <1.5h');
76+
});
77+
78+
// Test 9b: Should use custom target merge time when provided
79+
test('Uses custom target merge time when provided', () => {
80+
const result = formatR2RCallout(10, 50000, 60, 70, 2.0);
81+
assert(result.includes('reducing merge times to &lt;2.0h'), 'Should mention reducing merge times to <2.0h');
82+
});
83+
84+
// Test 10: Should contain proper HTML structure
85+
test('Contains proper HTML div wrapper', () => {
86+
const result = formatR2RCallout(10, 50000, 60, 70);
87+
assert(result.startsWith('<div'), 'Should start with <div');
88+
assert(result.endsWith('</div>'), 'Should end with </div>');
89+
});
90+
91+
// Test 11: Should use green color scheme
92+
test('Uses green color scheme', () => {
93+
const result = formatR2RCallout(10, 50000, 60, 70);
94+
assert(result.includes('#00c853'), 'Should include green color #00c853');
95+
});
96+
97+
console.log('\nAll tests passed! ✓');

0 commit comments

Comments
 (0)