Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions __tests__/tests/test-improvement.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const rule = require('../../src/rules/tests/test-improvement');

describe('Test Improvement Rule', () => {
it('labels when both source and tests change', () => {
const files = [{ filename: 'src/app.ts' }, { filename: '__tests__/app.test.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toContain('test-improvement');
});

it('labels for various source and test combinations', () => {
const files = [{ filename: 'lib/util.js' }, { filename: 'test/util.spec.js' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toContain('test-improvement');
});

it('does not label when only tests change', () => {
const files = [{ filename: '__tests__/app.test.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('does not label when only source changes', () => {
const files = [{ filename: 'src/app.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('does not label when only docs change', () => {
const files = [{ filename: 'README.md' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('handles empty files array', () => {
const labels = rule({ files: [], pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('handles debug mode', () => {
const files = [{ filename: 'src/app.js' }, { filename: '__tests__/app.test.js' }];
expect(() => rule({ files, pr: {}, enableDebug: true })).not.toThrow();
});
});
43 changes: 43 additions & 0 deletions __tests__/tests/test-missing.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const rule = require('../../src/rules/tests/test-missing');

describe('Test Missing Rule', () => {
it('labels when source changes without tests', () => {
const files = [{ filename: 'src/app.ts' }, { filename: 'src/util.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toContain('test-missing');
});

it('labels for various source file types', () => {
const files = [{ filename: 'lib/helper.js' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toContain('test-missing');
});

it('does not label when tests are present', () => {
const files = [{ filename: 'src/app.ts' }, { filename: '__tests__/app.test.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('does not label when only tests change', () => {
const files = [{ filename: '__tests__/app.test.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('does not label when only docs change', () => {
const files = [{ filename: 'README.md' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('handles empty files array', () => {
const labels = rule({ files: [], pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('handles debug mode', () => {
const files = [{ filename: 'src/app.js' }];
expect(() => rule({ files, pr: {}, enableDebug: true })).not.toThrow();
});
});
37 changes: 37 additions & 0 deletions __tests__/tests/test-only-change.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const rule = require('../../src/rules/tests/test-only-change');

describe('Test Only Change Rule', () => {
it('labels when only tests are changed', () => {
const files = [{ filename: '__tests__/a.test.js' }, { filename: 'tests/utils.spec.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toContain('test-only-change');
});

it('labels when test/ directory files change', () => {
const files = [{ filename: 'test/unit.js' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toContain('test-only-change');
});

it('does not label when source files also change', () => {
const files = [{ filename: '__tests__/a.test.js' }, { filename: 'src/app.ts' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('does not label when only source files change', () => {
const files = [{ filename: 'src/app.js' }];
const labels = rule({ files, pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('handles empty files array', () => {
const labels = rule({ files: [], pr: {}, enableDebug: false });
expect(labels).toEqual([]);
});

it('handles debug mode', () => {
const files = [{ filename: '__tests__/test.js' }];
expect(() => rule({ files, pr: {}, enableDebug: true })).not.toThrow();
});
});
39 changes: 39 additions & 0 deletions src/rules/tests/test-improvement.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Test Improvement Detection Rule
*
* Adds `test-improvement` when both source and test files changed.
*/

module.exports = function testImprovementRule({ files, pr, enableDebug }) {
const labels = [];

const isTestFile = (name) => /(^|\/).__tests__\//i.test(name) || /\.(test|spec)\.[a-z0-9]+$/i.test(name) || /(^|\/)tests?\//i.test(name);
const isSourceFile = (name) => /\.(js|jsx|ts|tsx|java|go|py|rb|rs|php|cpp|c|cs|scala|kt|swift|vue|svelte)$/i.test(name) && !isTestFile(name);

const names = (files || []).map(f => (f && f.filename ? String(f.filename).toLowerCase() : ''));
const hasSource = names.some(n => n && isSourceFile(n));
const hasTests = names.some(n => n && isTestFile(n));

if (hasSource && hasTests) {
labels.push('test-improvement');
}

if (enableDebug) {
console.log(`[Test Improvement Rule] hasSource=${hasSource} hasTests=${hasTests} → ${labels.join(', ') || 'none'}`);
}

return labels;
};

module.exports.metadata = {
name: 'Test Improvement Detection',
description: 'Detects PRs that modify tests alongside code changes',
labels: [
{ name: 'test-improvement', color: '0E8A16', description: 'Tests improved/added with code changes' }
],
author: 'pr-auto-labeler',
version: '1.0.0',
category: 'tests'
};


39 changes: 39 additions & 0 deletions src/rules/tests/test-missing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Source Changed Without Tests Detection Rule
*
* Adds `test-missing` when source files changed but no test files changed.
*/

module.exports = function testMissingRule({ files, pr, enableDebug }) {
const labels = [];

const isTestFile = (name) => /(^|\/)__tests__\//i.test(name) || /\.(test|spec)\.[a-z0-9]+$/i.test(name) || /(^|\/)tests?\//i.test(name);
const isSourceFile = (name) => /\.(js|jsx|ts|tsx|java|go|py|rb|rs|php|cpp|c|cs|scala|kt|swift|vue|svelte)$/i.test(name);

const names = (files || []).map(f => (f && f.filename ? String(f.filename).toLowerCase() : ''));
const hasSource = names.some(n => n && isSourceFile(n));
const hasTests = names.some(n => n && isTestFile(n));

if (hasSource && !hasTests) {
labels.push('test-missing');
}

if (enableDebug) {
console.log(`[Test Missing Rule] hasSource=${hasSource} hasTests=${hasTests} → ${labels.join(', ') || 'none'}`);
}

return labels;
};

module.exports.metadata = {
name: 'Source Changed Without Tests',
description: 'Detects code changes without accompanying tests',
labels: [
{ name: 'test-missing', color: 'D93F0B', description: 'Code changes without tests' }
],
author: 'pr-auto-labeler',
version: '1.0.0',
category: 'tests'
};


38 changes: 38 additions & 0 deletions src/rules/tests/test-only-change.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Test-Only Change Detection Rule
*
* Adds `test-only-change` when only test files are changed.
*/

module.exports = function testOnlyChangeRule({ files, pr, enableDebug }) {
const labels = [];

const isTestFile = (name) => /(^|\/)__tests__\//i.test(name) || /\.(test|spec)\.[a-z0-9]+$/i.test(name) || /(^|\/)tests?\//i.test(name);

const names = (files || []).map(f => (f && f.filename ? String(f.filename).toLowerCase() : ''));
const hasAny = names.length > 0;
const allAreTests = hasAny && names.every(n => n && isTestFile(n));

if (allAreTests) {
labels.push('test-only-change');
}

if (enableDebug) {
console.log(`[Test Only Change Rule] allAreTests=${allAreTests} → ${labels.join(', ') || 'none'}`);
}

return labels;
};

module.exports.metadata = {
name: 'Test-only Change Detection',
description: 'Detects PRs that modify only test files',
labels: [
{ name: 'test-only-change', color: '0E8A16', description: 'Only test files changed' }
],
author: 'pr-auto-labeler',
version: '1.0.0',
category: 'tests'
};


Loading