function runRuleForItem(item) {
			const flatConfigArrayOptions = {
				baseConfig,
			};

			if (item.filename) {
				flatConfigArrayOptions.basePath =
					path.parse(item.filename).root || void 0;
			}

			const configs = new FlatConfigArray(
				testerConfig,
				flatConfigArrayOptions,
			);

			/*
			 * Modify the returned config so that the parser is wrapped to catch
			 * access of the start/end properties. This method is called just
			 * once per code snippet being tested, so each test case gets a clean
			 * parser.
			 */
			configs[ConfigArraySymbol.finalizeConfig] = function (...args) {
				// can't do super here :(
				const proto = Object.getPrototypeOf(this);
				const calculatedConfig = proto[
					ConfigArraySymbol.finalizeConfig
				].apply(this, args);

				// wrap the parser to catch start/end property access
				if (calculatedConfig.language === jslang) {
					calculatedConfig.languageOptions.parser = wrapParser(
						calculatedConfig.languageOptions.parser,
					);
				}

				return calculatedConfig;
			};

			let code, filename, output, beforeAST, afterAST;

			if (typeof item === "string") {
				code = item;
			} else {
				code = item.code;

				/*
				 * Assumes everything on the item is a config except for the
				 * parameters used by this tester
				 */
				const itemConfig = { ...item };

				for (const parameter of RuleTesterParameters) {
					delete itemConfig[parameter];
				}

				/*
				 * Create the config object from the tester config and this item
				 * specific configurations.
				 */
				configs.push(itemConfig);
			}

			if (hasOwnProperty(item, "only")) {
				assert.ok(
					typeof item.only === "boolean",
					"Optional test case property 'only' must be a boolean",
				);
			}
			if (hasOwnProperty(item, "filename")) {
				assert.ok(
					typeof item.filename === "string",
					"Optional test case property 'filename' must be a string",
				);
				filename = item.filename;
			}

			let ruleConfig = 1;

			if (hasOwnProperty(item, "options")) {
				assert(Array.isArray(item.options), "options must be an array");
				ruleConfig = [1, ...item.options];
			}

			configs.push({
				rules: {
					[ruleId]: ruleConfig,
				},
			});

			let schema;

			try {
				schema = Config.getRuleOptionsSchema(rule);
			} catch (err) {
				err.message += metaSchemaDescription;
				throw err;
			}

			/*
			 * Check and throw an error if the schema is an empty object (`schema:{}`), because such schema
			 * doesn't validate or enforce anything and is therefore considered a possible error. If the intent
			 * was to skip options validation, `schema:false` should be set instead (explicit opt-out).
			 *
			 * For this purpose, a schema object is considered empty if it doesn't have any own enumerable string-keyed
			 * properties. While `ajv.compile()` does use enumerable properties from the prototype chain as well,
			 * it caches compiled schemas by serializing only own enumerable properties, so it's generally not a good idea
			 * to use inherited properties in schemas because schemas that differ only in inherited properties would end up
			 * having the same cache entry that would be correct for only one of them.
			 *
			 * At this point, `schema` can only be an object or `null`.
			 */
			if (schema && Object.keys(schema).length === 0) {
				throw new Error(
					`\`schema: {}\` is a no-op${metaSchemaDescription}`,
				);
			}

			/*
			 * Setup AST getters.
			 * The goal is to check whether or not AST was modified when
			 * running the rule under test.
			 */
			configs.push({
				plugins: {
					"rule-tester": {
						rules: {
							"validate-ast": {
								create() {
									return {
										Program(node) {
											beforeAST =
												cloneDeeplyExcludesParent(node);
										},
										"Program:exit"(node) {
											afterAST = node;
										},
									};
								},
							},
						},
					},
				},
			});

			if (schema) {
				ajv.validateSchema(schema);

				if (ajv.errors) {
					const errors = ajv.errors
						.map(error => {
							const field =
								error.dataPath[0] === "."
									? error.dataPath.slice(1)
									: error.dataPath;

							return `\t${field}: ${error.message}`;
						})
						.join("\n");

					throw new Error([
						`Schema for rule ${ruleName} is invalid:`,
						errors,
					]);
				}

				/*
				 * `ajv.validateSchema` checks for errors in the structure of the schema (by comparing the schema against a "meta-schema"),
				 * and it reports those errors individually. However, there are other types of schema errors that only occur when compiling
				 * the schema (e.g. using invalid defaults in a schema), and only one of these errors can be reported at a time. As a result,
				 * the schema is compiled here separately from checking for `validateSchema` errors.
				 */
				try {
					ajv.compile(schema);
				} catch (err) {
					throw new Error(
						`Schema for rule ${ruleName} is invalid: ${err.message}`,
					);
				}
			}

			// check for validation errors
			try {
				configs.normalizeSync();
				configs.getConfig("test.js");
			} catch (error) {
				error.message = `ESLint configuration in rule-tester is invalid: ${error.message}`;
				throw error;
			}

			// Verify the code.
			const { applyLanguageOptions, applyInlineConfig, finalize } =
				SourceCode.prototype;
			let messages;

			try {
				forbiddenMethods.forEach(methodName => {
					SourceCode.prototype[methodName] =
						throwForbiddenMethodError(
							methodName,
							SourceCode.prototype,
						);
				});

				messages = linter.verify(code, configs, filename);
			} finally {
				SourceCode.prototype.applyInlineConfig = applyInlineConfig;
				SourceCode.prototype.applyLanguageOptions =
					applyLanguageOptions;
				SourceCode.prototype.finalize = finalize;
			}

			const fatalErrorMessage = messages.find(m => m.fatal);

			assert(
				!fatalErrorMessage,
				`A fatal parsing error occurred: ${fatalErrorMessage && fatalErrorMessage.message}`,
			);

			// Verify if autofix makes a syntax error or not.
			if (messages.some(m => m.fix)) {
				output = SourceCodeFixer.applyFixes(code, messages).output;
				const errorMessageInFix = linter
					.verify(output, configs, filename)
					.find(m => m.fatal);

				assert(
					!errorMessageInFix,
					[
						"A fatal parsing error occurred in autofix.",
						`Error: ${errorMessageInFix && errorMessageInFix.message}`,
						"Autofix output:",
						output,
					].join("\n"),
				);
			} else {
				output = code;
			}

			return {
				messages,
				output,
				beforeAST,
				afterAST: cloneDeeplyExcludesParent(afterAST),
				configs,
				filename,
			};
		}
Run the rule for the given item
@param {string|Object} item Item to run the rule against
@throws {Error} If an invalid schema.
@returns {Object} Eslint run result
@private