Skip to content

Commit

Permalink
Add omit input, deprecate others (#15)
Browse files Browse the repository at this point in the history
* add omit input, deprecate others

* naming

* docs + additional dist checking

* quickstart
  • Loading branch information
robherley committed Apr 7, 2024
1 parent 89f9398 commit 929eebe
Show file tree
Hide file tree
Showing 13 changed files with 398 additions and 143 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: test

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

Expand All @@ -17,3 +15,14 @@ jobs:
node-version: 20
- run: npm ci
- run: npm test
- run: npm run build
- name: check dist
run: |
changed_files_count=$(git status --porcelain | wc -l)
if [ $changed_files_count -ne 0 ]; then
echo 'mismatched files from ncc generation! did you forget to run `npm run build`?' | tee -a $GITHUB_STEP_SUMMARY
echo '```diff' >> $GITHUB_STEP_SUMMARY
git diff >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
exit 1
fi
91 changes: 79 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,56 @@
# go-test-action

GitHub Action for running `go test ...` and getting rich summary and annotations as output.
- [go-test-action](#go-test-action)
- [Quick start](#quick-start)
- [Inputs](#inputs)
- [Screenshots](#screenshots)
- [Examples](#examples)
- [Basic](#basic)
- [Using existing test file](#using-existing-test-file)
- [Omitting elements](#omitting-elements)

GitHub Action for running `go test ./...` and getting rich summary and annotations as output.

Powered by [Job Summaries](https://github.blog/2022-05-09-supercharging-github-actions-with-job-summaries/), this Action will generate a convenient interactive viewer for tests based on Go's [test2json](https://pkg.go.dev/cmd/test2json) output. If there are any errors during `go test`, the Action will report back the same exit code, which will fail the job.

## Quick start

```yaml
- name: Test
uses: robherley/go-test-action@v0
```

## Inputs

| Input | Default | Description |
| - | - | - |
| moduleDirectory | `.` | relative path to the directory containing the `go.mod` of the module you wish to test |
| testArguments | `./...` | arguments to pass to `go test`, `-json` will be prepended automatically |
| fromJSONFile | null | parse an exisiting [test2json](https://pkg.go.dev/cmd/test2json) file, instead of executing go test |
| omitUntestedPackages | `false` | omit any go packages that don't have any tests from the summary output |
| omitSuccessfulPackages | `false` | omit any go packages that didn't contain failed tests |
| omitPie | `false` | omit the pie chart from the summary output
```yaml
- uses: robherley/go-test-action@v0
with:
# Relative path to the directory containing the go.mod of the module you wish to test.
# Optional. Default is '.'
moduleDirectory:

# Arguments to pass to go test, -json will be prepended automatically.
# Optional. Default is './...'
testArguments:

# Parse an exisiting [test2json](https://pkg.go.dev/cmd/test2json) file, instead of executing go test.
# Will always exit(0) on successful test file parse.
# Optional. No default
fromJSONFile:

# Whitespace separated list of renderable items to omit.
# Valid options to omit are:
# untested: packages that have no tests
# successful: packages that are successful
# pie: mermaid.js pie chart
# pkg-tests: per-package test list
# pkg-output: per-package test output
# stderr: standard error output of `go test` subprocess
# Optional. No default
omit:
```

## Demo
## Screenshots

Tests are organized per package, with a brief summary of individual test results:

Expand All @@ -25,7 +60,9 @@ Expand for per-test (with subtest) results and to view raw test output:

![summary expanded](docs/img/expanded.png)

## Example workflow
## Examples

### Basic

```yaml
name: Go
Expand All @@ -48,6 +85,36 @@ jobs:
run: go build -v ./...

- name: Test
uses: robherley/go-test-action@v0.3.0
uses: robherley/go-test-action@v0
```

### Using existing test file

```yaml
- name: Test
uses: robherley/go-test-action@v0
with:
fromJSONFile: /path/to/test2json.json
```

### Omitting elements

See [Inputs](#inputs) above for valid options

```yaml
- name: Test
uses: robherley/go-test-action@v0
with:
omit: |
pie
stderr
```

or

```yaml
- name: Test
uses: robherley/go-test-action@v0
with:
omit: 'pie'
```
3 changes: 1 addition & 2 deletions __tests__/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ export const removeSummaryFile = async () => {
export const setupActionsInputs = () => {
process.env['INPUT_MODULEDIRECTORY'] = testModuleDirectory
process.env['INPUT_TESTARGUMENTS'] = testArguments
process.env['INPUT_OMITUNTESTEDPACKAGES'] = 'false'
process.env['INPUT_OMITPIE'] = 'false'
process.env['INPUT_OMIT'] = ''
}

export const createFakeGoModule = async () => {
Expand Down
83 changes: 83 additions & 0 deletions __tests__/inputs.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as core from '@actions/core'
import { OmitOption, getInputs } from '../src/inputs'

jest.mock('@actions/core')

const mockGetInput = core.getInput as jest.MockedFunction<typeof core.getInput>
const mockGetBooleanInput = core.getBooleanInput as jest.MockedFunction<
typeof core.getBooleanInput
>

const mockInput = (name: string, value: string) => {
mockGetInput.mockImplementation((n: string) => (n === name ? value : ''))
}

describe('renderer', () => {
beforeEach(() => {
jest.resetAllMocks()
})

it('uses default values', () => {
mockGetInput.mockReturnValue('')
const inputs = getInputs()

expect(inputs).toEqual({
moduleDirectory: '.',
testArguments: ['./...'],
fromJSONFile: null,
omit: new Set(),
})
})

it('parses moduleDirectory', () => {
mockInput('moduleDirectory', 'foo')
const inputs = getInputs()

expect(inputs.moduleDirectory).toEqual('foo')
})

it('parses testArguments', () => {
mockInput('testArguments', 'foo bar')
const inputs = getInputs()

expect(inputs.testArguments).toEqual(['foo', 'bar'])
})

it('parses fromJSONFile', () => {
mockInput('fromJSONFile', 'foo.json')
const inputs = getInputs()

expect(inputs.fromJSONFile).toEqual('foo.json')
})

it('parses omit', () => {
mockInput(
'omit',
[...Object.values(OmitOption), 'foo', 'bar', 'baz'].join('\n')
)
const inputs = getInputs()

expect(inputs.omit).toEqual(new Set(Object.values(OmitOption)))
})

it('supports deprecated inputs', () => {
mockGetInput.mockImplementation((name: string) => {
switch (name) {
case 'omitUntestedPackages':
case 'omitSuccessfulPackages':
case 'omitPie':
return 'true'
default:
return ''
}
})

mockGetBooleanInput.mockReturnValue(true)

const inputs = getInputs()
expect(inputs.omit).toEqual(
new Set([OmitOption.Untested, OmitOption.Successful, OmitOption.Pie])
)
expect(core.warning).toHaveBeenCalled()
})
})
60 changes: 47 additions & 13 deletions __tests__/renderer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { parseTestEvents } from '../src/events'
import Renderer from '../src/renderer'
import { SummaryTableCell } from '@actions/core/lib/summary'
import { OmitOption } from '../src/inputs'

const loadSummaryHTML = async (): Promise<cheerio.CheerioAPI> => {
const file = await fs.readFile(testSummaryFilePath, { encoding: 'utf8' })
Expand All @@ -26,9 +27,7 @@ const getRenderer = async (): Promise<Renderer> => {
'github.com/robherley/go-test-example',
testEvents,
'', // stderr
false, // omitUntestedPackages
false, // omitSuccessfulPackages
false // omitPie
new Set()
)
}

Expand Down Expand Up @@ -105,9 +104,7 @@ describe('renderer', () => {
'github.com/robherley/empty-module',
[],
'',
false,
false,
false
new Set()
)
await renderer.writeSummary()

Expand Down Expand Up @@ -169,9 +166,9 @@ describe('renderer', () => {
expect($.text()).toContain(pieData)
})

it('does not render pie when omitPie is true', async () => {
it('does not render pie when pie in omit', async () => {
const renderer = await getRenderer()
renderer.omitPie = true
renderer.omit.add(OmitOption.Pie)
await renderer.writeSummary()
const $ = await loadSummaryHTML()

Expand Down Expand Up @@ -201,22 +198,22 @@ describe('renderer', () => {
})
})

it('renders correct number of table rows when omitUntestedPackages is true', async () => {
it('renders correct number of table rows when untested is in omit', async () => {
const renderer = await getRenderer()
renderer.omitUntestedPackages = true
renderer.omit.add(OmitOption.Untested)
await renderer.writeSummary()
const $ = await loadSummaryHTML()

expect($('tr')).toHaveLength(7)
})

it('renders correct number of table rows when omitUntestedPackages is true', async () => {
it('renders correct number of table rows when successful is in omit', async () => {
const renderer = await getRenderer()
renderer.omitUntestedPackages = true
renderer.omit.add(OmitOption.Successful)
await renderer.writeSummary()
const $ = await loadSummaryHTML()

expect($('tr')).toHaveLength(7)
expect($('tr')).toHaveLength(5)
})

it('does not render stderr when empty', async () => {
Expand All @@ -237,4 +234,41 @@ describe('renderer', () => {
expect($('summary:contains(Standard Error Output)')).toHaveLength(1)
expect($('details:contains(hello world)')).toHaveLength(1)
})

it('does not render stderr when in omit', async () => {
const renderer = await getRenderer()
renderer.omit.add(OmitOption.Stderr)
renderer.stderr = 'i should not be rendered'
await renderer.writeSummary()
const $ = await loadSummaryHTML()

expect($('summary:contains(Standard Error Output)')).toHaveLength(0)
})

it('renders package test and output list', async () => {
const renderer = await getRenderer()
await renderer.writeSummary()
const $ = await loadSummaryHTML()

expect($('summary:contains(🧪 Tests)')).toHaveLength(4)
expect($('summary:contains(🖨️ Output)')).toHaveLength(4)
})

it('does not render package test list when in omit', async () => {
const renderer = await getRenderer()
renderer.omit.add(OmitOption.PackageTests)
await renderer.writeSummary()
const $ = await loadSummaryHTML()

expect($('summary:contains(🧪 Tests)')).toHaveLength(0)
})

it('does not render package output list when in omit', async () => {
const renderer = await getRenderer()
renderer.omit.add(OmitOption.PackageOutput)
await renderer.writeSummary()
const $ = await loadSummaryHTML()

expect($('summary:contains(🖨️ Output)')).toHaveLength(0)
})
})
26 changes: 0 additions & 26 deletions __tests__/runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,6 @@ describe('runner', () => {
setupActionsInputs()
})

it("sets defaults if inputs aren't set", async () => {
delete process.env['INPUT_MODULEDIRECTORY']
delete process.env['INPUT_TESTARGUMENTS']
delete process.env['INPUT_OMITUNTESTEDPACKAGES']
delete process.env['INPUT_OMITPIE']

const runner = new Runner()
expect(runner.moduleDirectory).toBe('.')
expect(runner.testArguments).toEqual(['./...'])
expect(runner.omitUntestedPackages).toEqual(false)
expect(runner.omitPie).toEqual(false)
})

it('uses inputs if they are set', async () => {
process.env['INPUT_MODULEDIRECTORY'] = '/some/random/directory'
process.env['INPUT_TESTARGUMENTS'] = '-foo -bar\t-baz'
process.env['INPUT_OMITUNTESTEDPACKAGES'] = 'true'
process.env['INPUT_OMITPIE'] = 'true'

const runner = new Runner()
expect(runner.moduleDirectory).toBe('/some/random/directory')
expect(runner.testArguments).toEqual(['-foo', '-bar', '-baz'])
expect(runner.omitUntestedPackages).toEqual(true)
expect(runner.omitPie).toEqual(true)
})

it('resolves module name from go.mod', async () => {
const runner = new Runner()
const modName = await runner.findModuleName()
Expand Down
Loading

0 comments on commit 929eebe

Please sign in to comment.