Skip to content

Commit e05bdf5

Browse files
committed
test: add vitest setup and unit tests; chore: migrate to pnpm and add coverage
- Enable Corepack and pin pnpm via packageManager - Remove yarn.lock and install via pnpm - Add vitest and @vitest/coverage-v8; configure vitest - Refactor index.js to export pure functions and defer env validation - Add tests for buildChart and replaceCodestatsSection - Update CI workflow to use pnpm and run tests + coverage
1 parent f9ef4e4 commit e05bdf5

File tree

3,303 files changed

+533115
-455
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

3,303 files changed

+533115
-455
lines changed

.github/workflows/docker-publish.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,28 @@ jobs:
2626
steps:
2727
- uses: actions/checkout@v2
2828

29+
- name: Setup pnpm
30+
uses: pnpm/action-setup@v4
31+
with:
32+
version: 9.12.0
33+
run_install: false
34+
35+
- name: Setup Node
36+
uses: actions/setup-node@v4
37+
with:
38+
node-version: 20
39+
cache: pnpm
40+
41+
- name: Install dependencies
42+
run: pnpm install --frozen-lockfile
43+
2944
- name: Run tests
45+
run: pnpm test
46+
47+
- name: Run coverage
48+
run: pnpm coverage
49+
50+
- name: Build Docker image for testing
3051
run: |
3152
if [ -f docker-compose.test.yml ]; then
3253
docker-compose --file docker-compose.test.yml build

index.js

Lines changed: 103 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,65 +10,79 @@ if (typeof process.env.INPUT_DEBUG !== 'undefined') {
1010
console.log(process.env)
1111
}
1212

13-
// Validate environment varialbes
14-
if (typeof process.env.INPUT_CODESTATS_USERNAME == 'undefined')
15-
throw new Error('InvalidArgumentExcpetion – The CODESTATS_USERNAME has to be set!')
13+
/**
14+
* Create options from environment variables
15+
*/
16+
function createOptions() {
17+
// Validate environment variables
18+
if (typeof process.env.INPUT_CODESTATS_USERNAME == 'undefined')
19+
throw new Error('InvalidArgumentExcpetion – The CODESTATS_USERNAME has to be set!')
1620

17-
// Options
18-
const options = {
19-
codestats: {
20-
username: String(process.env.INPUT_CODESTATS_USERNAME),
21-
url: `https://codestats.net/api/users/${process.env.INPUT_CODESTATS_USERNAME}`,
22-
profile: `https://codestats.net/users/${process.env.INPUT_CODESTATS_USERNAME}`
23-
},
24-
git: {
25-
username: String(process.env.GITHUB_ACTOR) || 'CodeStats bot',
26-
message: String(process.env.INPUT_COMMIT_MESSAGE) || 'Update codestats metrics',
27-
token: String(process.env.INPUT_GITHUB_TOKEN)
28-
},
29-
graph: {
30-
width: Number(process.env.INPUT_GRAPH_WIDTH) || 42
31-
},
32-
readmeFile: String(process.env.INPUT_README_FILE) ? `${process.env.INPUT_README_FILE}` : `./README.md`,
33-
show: {
34-
title: Boolean(process.env.INPUT_SHOW_TITLE) || false,
35-
link: Boolean(process.env.INPUT_SHOW_LINK) || false
21+
return {
22+
codestats: {
23+
username: String(process.env.INPUT_CODESTATS_USERNAME),
24+
url: `https://codestats.net/api/users/${process.env.INPUT_CODESTATS_USERNAME}`,
25+
profile: `https://codestats.net/users/${process.env.INPUT_CODESTATS_USERNAME}`
26+
},
27+
git: {
28+
username: String(process.env.GITHUB_ACTOR) || 'CodeStats bot',
29+
message: String(process.env.INPUT_COMMIT_MESSAGE) || 'Update codestats metrics',
30+
token: String(process.env.INPUT_GITHUB_TOKEN)
31+
},
32+
graph: {
33+
width: Number(process.env.INPUT_GRAPH_WIDTH) || 42
34+
},
35+
readmeFile: String(process.env.INPUT_README_FILE) ? `${process.env.INPUT_README_FILE}` : `./README.md`,
36+
show: {
37+
title: Boolean(process.env.INPUT_SHOW_TITLE) || false,
38+
link: Boolean(process.env.INPUT_SHOW_LINK) || false
39+
}
3640
}
3741
}
3842

43+
// Lazily created options used by runtime start()
44+
let options = null
45+
3946
/**
4047
* Request callback
4148
*
4249
* @param {*} error
4350
* @param {*} response
4451
* @param {*} body
4552
*/
46-
const callback = function (error, response, body) {
47-
if (!error && response.statusCode == 200) {
48-
const languages = Object.entries(JSON.parse(body).languages)
49-
updateReadme(buildChart(languages), commitChanges)
53+
const makeCallback = function (opts) {
54+
return function (error, response, body) {
55+
if (!error && response.statusCode == 200) {
56+
const languages = Object.entries(JSON.parse(body).languages)
57+
const updateReadmeForOpts = makeUpdateReadme(opts)
58+
const commitChangesForOpts = makeCommitChanges(opts)
59+
updateReadmeForOpts(buildChart(languages, opts.graph.width), commitChangesForOpts)
60+
}
5061
}
5162
}
5263

5364
/**
5465
* Commit changes in README file
5566
*/
56-
const commitChanges = function () {
57-
const git = simpleGit()
58-
if (typeof process.env.INPUT_DEBUG !== 'undefined') {
59-
console.log('::: Commit changes')
60-
git.status()
67+
const makeCommitChanges = function (opts) {
68+
return function () {
69+
const git = simpleGit()
70+
if (typeof process.env.INPUT_DEBUG !== 'undefined') {
71+
console.log('::: Commit changes')
72+
git.status()
73+
}
74+
git.commit(opts.git.message, opts.readmeFile, { '--author': opts.git.username }).push()
6175
}
62-
git.commit(options.git.message, options.readmeFile, { '--author': options.git.username }).push()
6376
}
6477

6578
/**
6679
* Build chart with data
6780
*
6881
* @param {Array} data
82+
* @param {number} width
6983
* @returns {String}
7084
*/
71-
const buildChart = function (data) {
85+
const buildChart = function (data, width = 42) {
7286
let languageChart = {}
7387

7488
data.sort(function (a, b) {
@@ -80,7 +94,24 @@ const buildChart = function (data) {
8094
[key]: value.xps
8195
})
8296
})
83-
return bars(languageChart, { bar: '█', width: options.graph.width })
97+
return bars(languageChart, { bar: '█', width })
98+
}
99+
100+
/**
101+
* Replace the codestats section in markdown content
102+
*
103+
* @param {string} markdown
104+
* @param {string} content
105+
* @param {string} header
106+
* @param {string} footer
107+
* @returns {string}
108+
*/
109+
const replaceCodestatsSection = function (markdown, content, header = '', footer = '') {
110+
const replacement = `<!-- START_SECTION:codestats -->\n${header}\`\`\`text\n${content}\`\`\`\n${footer}<!-- END_SECTION:codestats -->`
111+
return markdown.replace(
112+
/((<!--.*START_SECTION:codestats.*-->)([\s\S]+)(<!--.*END_SECTION:codestats.*-->))/g,
113+
replacement
114+
)
84115
}
85116

86117
/**
@@ -90,26 +121,46 @@ const buildChart = function (data) {
90121
* @param {*} callback
91122
* @returns {void}
92123
*/
93-
const updateReadme = function (content, callback) {
94-
fs.readFile(options.readmeFile, 'utf8', function (err, data) {
95-
let header = options.show.title ? `*Language experience level (Last update ${new Date().toUTCString()})*\n\n` : '',
96-
footer = options.show.link ? `\n> My [CodeStats profile](${options.codestats.profile}) in detail.\n` : ''
124+
const makeUpdateReadme = function (opts) {
125+
return function (content, callback) {
126+
fs.readFile(opts.readmeFile, 'utf8', function (err, data) {
127+
let header = opts.show.title ? `*Language experience level (Last update ${new Date().toUTCString()})*\n\n` : '',
128+
footer = opts.show.link ? `\n> My [CodeStats profile](${opts.codestats.profile}) in detail.\n` : ''
97129

98-
if (err) {
99-
console.log(err)
100-
}
101-
let replacement = `<!-- START_SECTION:codestats -->\n${header}\`\`\`text\n${content}\`\`\`\n${footer}<!-- END_SECTION:codestats -->`
102-
let result = data.replace(
103-
/((<!--.*START_SECTION:codestats.*-->)([\s\S]+)(<!--.*END_SECTION:codestats.*-->))/g,
104-
replacement
105-
)
130+
if (err) {
131+
console.log(err)
132+
}
133+
const result = replaceCodestatsSection(data, content, header, footer)
106134

107-
fs.writeFile(options.readmeFile, result, 'utf8', function (err) {
108-
if (err) console.log(err)
109-
callback()
135+
fs.writeFile(opts.readmeFile, result, 'utf8', function (err) {
136+
if (err) console.log(err)
137+
callback()
138+
})
110139
})
111-
})
140+
}
141+
}
142+
143+
/**
144+
* Start the action if called directly
145+
*/
146+
function start() {
147+
const opts = createOptions()
148+
const callback = makeCallback(opts)
149+
request(opts.codestats, callback)
112150
}
113151

114-
// Init request
115-
request(options.codestats, callback)
152+
// Export functions for testing
153+
module.exports = {
154+
buildChart,
155+
replaceCodestatsSection,
156+
createOptions,
157+
makeCallback,
158+
makeUpdateReadme,
159+
makeCommitChanges,
160+
start
161+
}
162+
163+
// Init request if called directly
164+
if (require.main === module) {
165+
start()
166+
}

node_modules/.bin/sshpk-conv

Lines changed: 0 additions & 1 deletion
This file was deleted.

node_modules/.bin/sshpk-conv

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/sshpk-sign

Lines changed: 0 additions & 1 deletion
This file was deleted.

node_modules/.bin/sshpk-sign

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/sshpk-verify

Lines changed: 0 additions & 1 deletion
This file was deleted.

node_modules/.bin/sshpk-verify

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

node_modules/.bin/uuid

Lines changed: 0 additions & 1 deletion
This file was deleted.

node_modules/.bin/uuid

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)