diff --git a/README.md b/README.md index 75f32df..4acef3a 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,8 @@ don't allow this because they don't work on a level of individual jobs or steps. run: ... ``` +For a more complete example, see [sample-filters.yml](./sample-filters.yml). + For more scenarios see [examples](#examples) section. ## Notes diff --git a/__tests__/csv-escape.test.ts b/__tests__/csv-escape.test.ts index f6dc6df..21a7c3a 100644 --- a/__tests__/csv-escape.test.ts +++ b/__tests__/csv-escape.test.ts @@ -1,6 +1,9 @@ import {csvEscape} from '../src/list-format/csv-escape' describe('csvEscape() backslash escapes every character except subset of definitely safe characters', () => { + test('empty string is returned unchanged', () => { + expect(csvEscape('')).toBe('') + }) test('simple filename should not be modified', () => { expect(csvEscape('file.txt')).toBe('file.txt') }) diff --git a/__tests__/export-format.test.ts b/__tests__/export-format.test.ts new file mode 100644 index 0000000..acc8084 --- /dev/null +++ b/__tests__/export-format.test.ts @@ -0,0 +1,42 @@ +import {ChangeStatus} from '../src/file' +import {exportResults} from '../src/main' +import * as core from '@actions/core' + +jest.mock('@actions/core', () => ({ + info: jest.fn(), + startGroup: jest.fn(), + endGroup: jest.fn(), + setOutput: jest.fn() +})) + +describe('exportResults file listing formats', () => { + const files = [ + {filename: 'simple.txt', status: ChangeStatus.Modified}, + {filename: 'file with space.txt', status: ChangeStatus.Added} + ] + const results = {sample: files} + + beforeEach(() => { + jest.clearAllMocks() + }) + + test('exports csv formatted list', () => { + exportResults(results, 'csv') + expect(core.setOutput).toHaveBeenCalledWith('sample_files', 'simple.txt,"file with space.txt"') + }) + + test('exports json formatted list', () => { + exportResults(results, 'json') + expect(core.setOutput).toHaveBeenCalledWith('sample_files', JSON.stringify(['simple.txt', 'file with space.txt'])) + }) + + test('exports shell escaped list', () => { + exportResults(results, 'shell') + expect(core.setOutput).toHaveBeenCalledWith('sample_files', "simple.txt 'file with space.txt'") + }) + + test('exports escape formatted list', () => { + exportResults(results, 'escape') + expect(core.setOutput).toHaveBeenCalledWith('sample_files', 'simple.txt file\\ with\\ space.txt') + }) +}) diff --git a/__tests__/fixtures/sample-filter.yml b/__tests__/fixtures/sample-filter.yml new file mode 100644 index 0000000..bc602ea --- /dev/null +++ b/__tests__/fixtures/sample-filter.yml @@ -0,0 +1,2 @@ +sample: + - 'src/**/*.ts' diff --git a/__tests__/git.test.ts b/__tests__/git.test.ts index 9645221..c88a5a9 100644 --- a/__tests__/git.test.ts +++ b/__tests__/git.test.ts @@ -14,6 +14,17 @@ describe('parsing output of the git diff command', () => { expect(files[2].filename).toBe('src/main.ts') expect(files[2].status).toBe(ChangeStatus.Deleted) }) + + test('parseGitDiffOutput handles copied, renamed and unmerged statuses', async () => { + const files = git.parseGitDiffOutput( + 'C\u0000src/copied.ts\u0000' + 'R\u0000src/renamed.ts\u0000' + 'U\u0000src/conflict.ts\u0000' + ) + expect(files).toEqual([ + {filename: 'src/copied.ts', status: ChangeStatus.Copied}, + {filename: 'src/renamed.ts', status: ChangeStatus.Renamed}, + {filename: 'src/conflict.ts', status: ChangeStatus.Unmerged} + ]) + }) }) describe('git utility function tests (those not invoking git)', () => { diff --git a/__tests__/predicate-quantifier.test.ts b/__tests__/predicate-quantifier.test.ts new file mode 100644 index 0000000..be3b66e --- /dev/null +++ b/__tests__/predicate-quantifier.test.ts @@ -0,0 +1,15 @@ +import {isPredicateQuantifier, SUPPORTED_PREDICATE_QUANTIFIERS} from '../src/filter' + +describe('isPredicateQuantifier', () => { + test('returns true for supported values', () => { + for (const value of SUPPORTED_PREDICATE_QUANTIFIERS) { + expect(isPredicateQuantifier(value)).toBe(true) + } + }) + + test('returns false for unsupported values including sample', () => { + expect(isPredicateQuantifier('sample')).toBe(false) + expect(isPredicateQuantifier('')).toBe(false) + expect(isPredicateQuantifier(undefined)).toBe(false) + }) +}) diff --git a/__tests__/sample-config.test.ts b/__tests__/sample-config.test.ts new file mode 100644 index 0000000..8a2a90a --- /dev/null +++ b/__tests__/sample-config.test.ts @@ -0,0 +1,15 @@ +import * as fs from 'fs' +import * as path from 'path' +import {Filter} from '../src/filter' +import {ChangeStatus} from '../src/file' + +describe('sample configuration file', () => { + test('parses filter rules from sample file', () => { + const yamlPath = path.join(__dirname, 'fixtures', 'sample-filter.yml') + const yaml = fs.readFileSync(yamlPath, 'utf8') + const filter = new Filter(yaml) + const files = [{filename: 'src/index.ts', status: ChangeStatus.Modified}] + const match = filter.match(files) + expect(match.sample).toEqual(files) + }) +}) diff --git a/__tests__/sample.test.ts b/__tests__/sample.test.ts new file mode 100644 index 0000000..85e4a43 --- /dev/null +++ b/__tests__/sample.test.ts @@ -0,0 +1,26 @@ +import {Filter} from '../src/filter' +import {ChangeStatus} from '../src/file' + +describe('sample filter usage', () => { + test('matches files in sample folder', () => { + const yaml = ` + sample: + - sample/** + ` + const filter = new Filter(yaml) + const files = [{filename: 'sample/example.ts', status: ChangeStatus.Modified}] + const match = filter.match(files) + expect(match.sample).toEqual(files) + }) + + test('does not match files outside sample folder', () => { + const yaml = ` + sample: + - sample/** + ` + const filter = new Filter(yaml) + const files = [{filename: 'other/example.ts', status: ChangeStatus.Modified}] + const match = filter.match(files) + expect(match.sample).toEqual([]) + }) +}) diff --git a/__tests__/shell-escape.test.ts b/__tests__/shell-escape.test.ts index ece4c37..b0c230d 100644 --- a/__tests__/shell-escape.test.ts +++ b/__tests__/shell-escape.test.ts @@ -1,6 +1,9 @@ import {backslashEscape, shellEscape} from '../src/list-format/shell-escape' describe('escape() backslash escapes every character except subset of definitely safe characters', () => { + test('empty string is returned unchanged', () => { + expect(backslashEscape('')).toBe('') + }) test('simple filename should not be modified', () => { expect(backslashEscape('file.txt')).toBe('file.txt') }) diff --git a/sample-filters.yml b/sample-filters.yml new file mode 100644 index 0000000..cf5a3a3 --- /dev/null +++ b/sample-filters.yml @@ -0,0 +1,11 @@ +# Sample filters configuration for paths-filter action +# Demonstrates basic usage of filters and change status qualifiers +shared: &shared + - common/** + - config/** +backend: + - *shared + - src/backend/** +docs: + - added: docs/** + - modified: README.md