Repository: eslint/eslint
File: lib/rule-tester/rule-tester.js (JavaScript)

Code before:
         */
        function assertASTDidntChange(beforeAST, afterAST) {
            if (!equal(beforeAST, afterAST)) {
                assert.fail("Rule should not modify AST.");
            }
        }

        /**
         * Check if this test case is a duplicate of one we have seen before.
         * @param {string|Object} item test case object
         * @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
         * @returns {void}
         * @private
         */
        function checkDuplicateTestCase(item, seenTestCases) {
            if (!isSerializable(item)) {

                /*
                 * If we can't serialize a test case (because it contains a function, RegExp, etc), skip the check.
                 * This might happen with properties like: options, plugins, settings, languageOptions.parser, languageOptions.parserOptions.
                 */
                return;
            }

            const normalizedItem = typeof item === "string" ? { code: item } : item;
            const serializedTestCase = stringify(normalizedItem, { replacer: (key, value) => (!duplicationIgnoredParameters.has(key) ? value : void 0) });

            assert(
                !seenTestCases.has(serializedTestCase),
                "detected duplicate test case"
            );
            seenTestCases.add(serializedTestCase);
        }

        /**
         * Check if the template is valid or not
         * all valid cases go through this
         * @param {string|Object} item Item to run the rule against
         * @returns {void}
         * @private
         */
        function testValidTemplate(item) {
            const code = typeof item === "object" ? item.code : item;

            assert.ok(typeof code === "string", "Test case must specify a string value for 'code'");
            if (item.name) {
                assert.ok(typeof item.name === "string", "Optional test case property 'name' must be a string");
            }

            checkDuplicateTestCase(item, seenValidTestCases);


Review comment (refactor):
We should only exclude top-level test case properties. Otherwise, if the rule has options with these names, test cases like the following would be mistakenly detected as duplicates:

```js
{
    code: "var foo;",
    options: [{ name: "bar" }]
},
{
    code: "var foo;",
    options: [{ name: "baz" }]
}
```

Code after:
         */
        function assertASTDidntChange(beforeAST, afterAST) {
            if (!equal(beforeAST, afterAST)) {
                assert.fail("Rule should not modify AST.");
            }
        }

        /**
         * Check if this test case is a duplicate of one we have seen before.
         * @param {string|Object} item test case object
         * @param {Set<string>} seenTestCases set of serialized test cases we have seen so far (managed by this function)
         * @returns {void}
         * @private
         */
        function checkDuplicateTestCase(item, seenTestCases) {
            if (!isSerializable(item)) {

                /*
                 * If we can't serialize a test case (because it contains a function, RegExp, etc), skip the check.
                 * This might happen with properties like: options, plugins, settings, languageOptions.parser, languageOptions.parserOptions.
                 */
                return;
            }

            const normalizedItem = typeof item === "string" ? { code: item } : item;
            const serializedTestCase = stringify(normalizedItem, {
                replacer(key, value) {

                    // "this" is the currently stringified object --> only ignore top-level properties
                    return (normalizedItem !== this || !duplicationIgnoredParameters.has(key)) ? value : void 0;
                }
            });

            assert(
                !seenTestCases.has(serializedTestCase),
                "detected duplicate test case"
            );
            seenTestCases.add(serializedTestCase);
        }

        /**
         * Check if the template is valid or not
         * all valid cases go through this
         * @param {string|Object} item Item to run the rule against
         * @returns {void}
         * @private
         */
        function testValidTemplate(item) {
            const code = typeof item === "object" ? item.code : item;

            assert.ok(typeof code === "string", "Test case must specify a string value for 'code'");