Skip to content
Open
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
2 changes: 2 additions & 0 deletions packages/core/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import noDatepipeTransformFormatArg from './rules/no-datepipe-transform-format-a
import noInlineDateFormat from './rules/no-inline-date-format.js';
import noHardcodedStrings from './rules/no-hardcoded-strings.js';
import noDateCurrencyMutation from './rules/no-date-currency-mutation.js';
import requireTestAssigneeComment from './rules/require-test-assignee-comment.js';

const rules = {
'i18n-key-naming-convention': i18nKeyNamingConvention,
Expand All @@ -12,6 +13,7 @@ const rules = {
'no-inline-date-format': noInlineDateFormat,
'no-hardcoded-strings': noHardcodedStrings,
'no-date-currency-mutation': noDateCurrencyMutation,
'require-test-assignee-comment': requireTestAssigneeComment,
};

const configs = {
Expand Down
67 changes: 67 additions & 0 deletions packages/core/src/rules/require-test-assignee-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ESLintUtils, AST_NODE_TYPES } from '@typescript-eslint/utils';

const createRule = ESLintUtils.RuleCreator(
() => 'https://github.com/fylein/fyle-eslint-plugin/blob/main/packages/docs/rules/require-test-assignee-comment.md',
);

const RULE_NAME = 'require-test-assignee-comment';

const ASSIGNEE_COMMENT_PATTERN = /Assignee:\s+@[\w.-]+/i;

export default createRule({
name: RULE_NAME,
meta: {
type: 'problem',
docs: {
description: 'Require an explicit assignee comment for Playwright `test.fixme` and `test.fail` cases.',
recommended: 'recommended',
},
schema: [],
messages: {
missingAssignee:
'test.fixme/test.fail must include an explicit assignee comment immediately above the call, e.g. "// @assignee @username".',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Should the comment include @assignee or just assignee?
  • Will it work for multiline comment?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plus 1

},
},
defaultOptions: [],
create(context) {
const sourceCode = context.getSourceCode();

function isTestFixmeOrFailCall(node) {
if (node.type !== AST_NODE_TYPES.CallExpression) {
return false;
}

const callee = node.callee;
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
return false;
}

const object = callee.object;
const property = callee.property;
if (object.type !== AST_NODE_TYPES.Identifier || object.name !== 'test') {
return false;
}

return property.type === AST_NODE_TYPES.Identifier && (property.name === 'fixme' || property.name === 'fail');
}

function hasAssigneeComment(node) {
const statement = node.parent && node.parent.type === AST_NODE_TYPES.ExpressionStatement ? node.parent : node;
const leadingComments = sourceCode.getCommentsBefore(statement);

return leadingComments.some((comment) => ASSIGNEE_COMMENT_PATTERN.test(comment.value));
}

return {
CallExpression(node) {
if (!isTestFixmeOrFailCall(node)) {
return;
}

if (!hasAssigneeComment(node)) {
context.report({ node, messageId: 'missingAssignee' });
}
},
};
},
});
39 changes: 39 additions & 0 deletions packages/core/src/tests/require-test-assignee-comment.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { RuleTester } from '@typescript-eslint/rule-tester';
import tsParser from '@typescript-eslint/parser';
import rule from '../rules/require-test-assignee-comment.js';

const ruleTester = new RuleTester({
languageOptions: {
parser: tsParser,
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
},
},
});

ruleTester.run('require-test-assignee-comment', rule, {
valid: [
`// Assignee: @arjun\n test.fixme('known issue', async ({ page }) => {});`,
`test('works normally', async ({ page }) => {});`,
`test.skip('skipped test without fail', async ({ page }) => {});`,
],
invalid: [
{
code: `test.fixme('known issue', async ({ page }) => {});`,
errors: [{ messageId: 'missingAssignee' }],
},
{
code: `// TODO: temporary bug\n test.fail('unstable feature', async ({ page }) => {});`,
errors: [{ messageId: 'missingAssignee' }],
},
{
code: `// @owner @aniruddha\n test.fail('should retry after failure', async ({ page }) => {});`,
errors: [{ messageId: 'missingAssignee' }],
},
{
code: `/* @responsible @omkar */\n test.fail('known bug', async ({ page }) => {});`,
errors: [{ messageId: 'missingAssignee' }],
},
],
});
52 changes: 52 additions & 0 deletions packages/docs/rules/require-test-assignee-comment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Custom ESLint Rule: require-test-assignee-comment

This rule ensures that Playwright `test.fixme` and `test.fail` annotations are never left orphaned by requiring an explicit assignee comment.

## What it checks

- `test.fixme(...)`
- `test.fail(...)`

The rule reports when these calls are present without a nearby comment assigning ownership.

## Accepted comment formats

Valid examples:

```js
// Assignee: @username
```

Only the `@assignee` syntax is accepted by this rule.

## Example

### ✅ Valid

```js
// Assignee: @arjun
test.fixme('known issue', async ({ page }) => {});
```

### ❌ Invalid

```js
test.fixme('known issue', async ({ page }) => {});
```

## Configuration

```js
import fyleCore from '@fyle/eslint-plugin';

export default [
{
plugins: {
'@fyle': fyleCore,
},
rules: {
'@fyle/require-test-assignee-comment': 'error',
},
},
];
```
Loading