From 626f0225ab718afcf396a5641857d642a8b077c0 Mon Sep 17 00:00:00 2001 From: Yavor Ivanov Date: Wed, 13 Nov 2024 10:16:03 +0200 Subject: [PATCH] feat: Node API in ui5 linter (#400) JIRA: CPOUI5FOUNDATION-885 --------- Co-authored-by: Merlin Beutlberger --- .github/workflows/ci.yml | 3 + ava-e2e.config.js | 5 + ava.config.js | 1 - eslint.config.js | 4 + package.json | 10 +- src/cli/base.ts | 12 +- src/index.ts | 70 + src/linter/LinterContext.ts | 13 +- src/linter/lintWorkspace.ts | 2 +- src/linter/linter.ts | 60 +- test/e2e/package-exports.ts | 22 + test/lib/cli/base.ts | 24 +- test/lib/index.ts | 149 ++ test/lib/linter/_linterHelper.ts | 4 +- test/lib/linter/linter.ts | 50 +- test/lib/linter/xmlTemplate/_helper.ts | 2 +- test/lib/snapshots/index.ts.md | 2424 ++++++++++++++++++++++++ test/lib/snapshots/index.ts.snap | Bin 0 -> 12269 bytes tsconfig.base.json | 3 + tsconfig.json | 4 + 20 files changed, 2783 insertions(+), 79 deletions(-) create mode 100644 ava-e2e.config.js create mode 100644 src/index.ts create mode 100644 test/e2e/package-exports.ts create mode 100644 test/lib/index.ts create mode 100644 test/lib/snapshots/index.ts.md create mode 100644 test/lib/snapshots/index.ts.snap diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d8fd1feb5..0747f9f07 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,3 +38,6 @@ jobs: - name: Send report to Coveralls for package @ui5/linter uses: coverallsapp/github-action@v2.3.4 + + - name: Run e2e tests + run: npm run e2e diff --git a/ava-e2e.config.js b/ava-e2e.config.js new file mode 100644 index 000000000..f4809c9ca --- /dev/null +++ b/ava-e2e.config.js @@ -0,0 +1,5 @@ +import defaultAvaConfig from "./ava.config.js"; + +defaultAvaConfig.files = ["test/e2e/**/*.ts"]; + +export default defaultAvaConfig; diff --git a/ava.config.js b/ava.config.js index 4e4059f8c..9c63376e3 100644 --- a/ava.config.js +++ b/ava.config.js @@ -1,4 +1,3 @@ -// Calculate nodeArguments based on the Node version const nodeArguments = [ "--import=tsx/esm", "--no-warnings=ExperimentalWarning", diff --git a/eslint.config.js b/eslint.config.js index e19a3e8c4..9cc20f584 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -19,6 +19,10 @@ export default tseslint.config( "test/tmp/*", "test/projects/*", "test/fixtures/*", + // This file must be excluded as it tests the package exports by + // requiring the package itself, which causes a circular dependency + // and TypeScript/ESlint gets confused during compilation. + "test/e2e/package-exports.ts", // Exclude generated code "lib/*", diff --git a/package.json b/package.json index f1f98be52..ce8789fae 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "ui5lint": "bin/ui5lint.js" }, "type": "module", + "types": "lib/index.d.ts", "scripts": { "build": "npm run cleanup && tsc -p tsconfig.build.json", "build-test": "tsc --noEmit -p .", @@ -33,13 +34,14 @@ "check-licenses": "licensee --errors-only", "cleanup": "rimraf lib coverage", "coverage": "nyc ava --node-arguments=\"--experimental-loader=@istanbuljs/esm-loader-hook\"", - "depcheck": "depcheck --ignores @commitlint/config-conventional,@istanbuljs/esm-loader-hook,rimraf,sap,mycomp", + "depcheck": "depcheck --ignores @commitlint/config-conventional,@istanbuljs/esm-loader-hook,rimraf,sap,mycomp,@ui5/linter", "hooks:pre-push": "npm run lint:commit", "lint": "eslint .", "lint:commit": "commitlint -e", "prepare": "node ./.husky/skip.js || husky", - "test": "npm run lint && npm run build-test && npm run coverage && npm run depcheck", + "test": "npm run lint && npm run build-test && npm run coverage && npm run e2e && npm run depcheck", "unit": "ava", + "e2e": "npm run build && ava --config ava-e2e.config.js", "unit-debug": "ava debug", "unit-update-snapshots": "ava --update-snapshots", "unit-watch": "ava --watch", @@ -57,6 +59,10 @@ "node": "^20.11.0 || >=22.0.0", "npm": ">= 8" }, + "exports": { + ".": "./lib/index.js", + "./package.json": "./package.json" + }, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.25", diff --git a/src/cli/base.ts b/src/cli/base.ts index e9b1e84cd..d8aed5006 100644 --- a/src/cli/base.ts +++ b/src/cli/base.ts @@ -135,7 +135,7 @@ async function handleLint(argv: ArgumentsCamelCase) { const { files: filePatterns, coverage, - ignorePattern, + ignorePattern: ignorePatterns, details, format, config, @@ -152,15 +152,15 @@ async function handleLint(argv: ArgumentsCamelCase) { const res = await lintProject({ rootDir: path.join(process.cwd()), - ignorePattern, + ignorePatterns, filePatterns, - reportCoverage, - includeMessageDetails: details, + coverage: reportCoverage, + details, configPath: config, - ui5ConfigPath: ui5Config, + ui5Config, }); - if (reportCoverage) { + if (coverage) { const coverageFormatter = new Coverage(); await writeFile("ui5lint-report.html", await coverageFormatter.format(res)); } diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 000000000..bcd4c9e04 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,70 @@ +import {lintProject} from "./linter/linter.js"; +import type {LintResult} from "./linter/LinterContext.js"; + +export type {LintResult} from "./linter/LinterContext.js"; + +// Define a separate interface for the Node API as there could be some differences +// in the options and behavior compared to LinterOptions internal type. +export interface UI5LinterOptions { + /** + * List of patterns to lint. + */ + filePatterns?: string[]; + /** + * Pattern/files that will be ignored during linting. + */ + ignorePatterns?: string[]; + /** + * Provides complementary information for each finding, if available + * @default false + */ + details?: boolean; + /** + * Path to a ui5lint.config.(cjs|mjs|js) file + */ + config?: string; + /** + * Whether to skip loading of the ui5lint.config.(cjs|mjs|js) config file + * @default false + */ + noConfig?: boolean; + /** + * Whether to provide a coverage report + * @default false + */ + coverage?: boolean; + /** + * Path to a ui5.yaml file or an object representation of ui5.yaml + * @default "./ui5.yaml" + */ + ui5Config?: string | object; + /** + * Root directory of the project + * @default process.cwd() + */ + rootDir?: string; +} + +export async function ui5lint(options?: UI5LinterOptions): Promise { + const { + filePatterns, + ignorePatterns = [], + details = false, + config, + noConfig, + coverage = false, + ui5Config = "./ui5.yaml", + rootDir = process.cwd(), + } = options ?? {}; + + return lintProject({ + rootDir, + filePatterns, + ignorePatterns, + coverage, + details, + configPath: config, + noConfig, + ui5Config, + }); +} diff --git a/src/linter/LinterContext.ts b/src/linter/LinterContext.ts index 05840a79b..09f053b1e 100644 --- a/src/linter/LinterContext.ts +++ b/src/linter/LinterContext.ts @@ -59,11 +59,12 @@ export interface TranspileResult { export interface LinterOptions { rootDir: string; filePatterns?: FilePattern[]; - ignorePattern?: FilePattern[]; - reportCoverage?: boolean; - includeMessageDetails?: boolean; + ignorePatterns?: FilePattern[]; + coverage?: boolean; + details?: boolean; configPath?: string; - ui5ConfigPath?: string; + noConfig?: boolean; + ui5Config?: string | object; namespace?: string; } @@ -111,8 +112,8 @@ export default class LinterContext { constructor(options: LinterOptions) { this.#rootDir = options.rootDir; this.#namespace = options.namespace; - this.#reportCoverage = !!options.reportCoverage; - this.#includeMessageDetails = !!options.includeMessageDetails; + this.#reportCoverage = !!options.coverage; + this.#includeMessageDetails = !!options.details; } getRootDir(): string { diff --git a/src/linter/lintWorkspace.ts b/src/linter/lintWorkspace.ts index a15b2f30b..e5b2bf162 100644 --- a/src/linter/lintWorkspace.ts +++ b/src/linter/lintWorkspace.ts @@ -33,7 +33,7 @@ export default async function lintWorkspace( patternsMatch, }); reader = resolveReader({ - patterns: options.ignorePattern ?? [], + patterns: options.ignorePatterns ?? [], resourceReader: reader, patternsMatch, relFsBasePath: relFsBasePath ?? "", diff --git a/src/linter/linter.ts b/src/linter/linter.ts index 156f19e75..31e347e58 100644 --- a/src/linter/linter.ts +++ b/src/linter/linter.ts @@ -12,16 +12,19 @@ import ConfigManager, {UI5LintConfigType} from "../utils/ConfigManager.js"; import {Minimatch} from "minimatch"; export async function lintProject({ - rootDir, filePatterns, ignorePattern, reportCoverage, includeMessageDetails, configPath, ui5ConfigPath, + rootDir, filePatterns, ignorePatterns, coverage, details, configPath, ui5Config, noConfig, }: LinterOptions): Promise { - const configMngr = new ConfigManager(rootDir, configPath); - const config = await configMngr.getConfiguration(); + let config: UI5LintConfigType = {}; + if (noConfig !== true) { + const configMngr = new ConfigManager(rootDir, configPath); + config = await configMngr.getConfiguration(); + } // In case path is set both by CLI and config use CLI - ui5ConfigPath = ui5ConfigPath ?? config.ui5Config; + ui5Config = ui5Config ?? config.ui5Config; const projectGraphDone = taskStart("Project Graph creation"); - const graph = await getProjectGraph(rootDir, ui5ConfigPath); + const graph = await getProjectGraph(rootDir, ui5Config); const project = graph.getRoot(); projectGraphDone(); @@ -61,10 +64,12 @@ export async function lintProject({ rootDir, namespace: project.getNamespace(), filePatterns, - ignorePattern, - reportCoverage, - includeMessageDetails, + ignorePatterns, + coverage, + details, configPath, + noConfig, + ui5Config, relFsBasePath, virBasePath, relFsBasePathTest, virBasePathTest, }, config); @@ -80,10 +85,13 @@ export async function lintProject({ } export async function lintFile({ - rootDir, filePatterns, ignorePattern, namespace, reportCoverage, includeMessageDetails, configPath, + rootDir, filePatterns, ignorePatterns, namespace, coverage, details, configPath, noConfig, }: LinterOptions): Promise { - const configMngr = new ConfigManager(rootDir, configPath); - const config = await configMngr.getConfiguration(); + let config: UI5LintConfigType = {}; + if (noConfig !== true) { + const configMngr = new ConfigManager(rootDir, configPath); + config = await configMngr.getConfiguration(); + } const virBasePath = namespace ? `/resources/${namespace}/` : "/"; const reader = createReader({ @@ -95,9 +103,9 @@ export async function lintFile({ rootDir, namespace, filePatterns, - ignorePattern, - reportCoverage, - includeMessageDetails, + ignorePatterns, + coverage, + details, configPath, relFsBasePath: "", virBasePath, @@ -117,7 +125,7 @@ async function lint( config: UI5LintConfigType ): Promise { const lintEnd = taskStart("Linting"); - let {ignorePattern, filePatterns} = options; + let {ignorePatterns, filePatterns} = options; const {relFsBasePath, virBasePath, relFsBasePathTest, virBasePathTest} = options; // Resolve files to include @@ -129,15 +137,15 @@ async function lint( const matchedPatterns = new Set(); // Resolve ignores - ignorePattern = [ + ignorePatterns = [ ...(config.ignores ?? []), - ...(ignorePattern ?? []), // CLI patterns go after config patterns + ...(ignorePatterns ?? []), // CLI patterns go after config patterns ].filter(($) => $); // Apply ignores to the workspace reader. // TypeScript needs the full context to provide correct analysis. // so, we can do filtering later via the filePathsReader const reader = resolveReader({ - patterns: ignorePattern, + patterns: ignorePatterns, resourceReader, patternsMatch: matchedPatterns, relFsBasePath, virBasePath, relFsBasePathTest, virBasePathTest, @@ -152,7 +160,7 @@ async function lint( relFsBasePath, virBasePath, relFsBasePathTest, virBasePathTest, }); filePathsReader = resolveReader({ - patterns: ignorePattern, + patterns: ignorePatterns, resourceReader: filePathsReader, patternsMatch: matchedPatterns, relFsBasePath, virBasePath, relFsBasePathTest, virBasePathTest, @@ -170,13 +178,19 @@ async function lint( return res; } -async function getProjectGraph(rootDir: string, ui5ConfigPath?: string): Promise { +async function getProjectGraph(rootDir: string, ui5Config?: string | object): Promise { let rootConfigPath, rootConfiguration; - const ui5YamlPath = ui5ConfigPath ? path.join(rootDir, ui5ConfigPath) : path.join(rootDir, "ui5.yaml"); - if (await fileExists(ui5YamlPath)) { + let ui5YamlPath; + if (typeof ui5Config !== "object") { + ui5YamlPath = ui5Config ? path.join(rootDir, ui5Config) : path.join(rootDir, "ui5.yaml"); + } + + if (typeof ui5Config === "object") { + rootConfiguration = ui5Config; + } else if (ui5YamlPath && await fileExists(ui5YamlPath)) { rootConfigPath = ui5YamlPath; } else { - if (ui5ConfigPath) throw new Error(`Unable to find UI5 config file '${ui5ConfigPath}'`); + if (ui5Config) throw new Error(`Unable to find UI5 config file '${ui5Config}'`); const isApp = await dirExists(path.join(rootDir, "webapp")); if (isApp) { rootConfiguration = { diff --git a/test/e2e/package-exports.ts b/test/e2e/package-exports.ts new file mode 100644 index 000000000..6013c53fe --- /dev/null +++ b/test/e2e/package-exports.ts @@ -0,0 +1,22 @@ +import test from "ava"; +import {createRequire} from "node:module"; + +// Using CommonsJS require since JSON module imports are still experimental +const require = createRequire(import.meta.url); + +test.serial("Package exports: export of package.json", (t) => { + t.truthy(require("@ui5/linter/package.json").version); +}); + +// Check number of defined exports +test.serial("Package exports: check number of exports", (t) => { + const packageJson = require("@ui5/linter/package.json"); + t.is(Object.keys(packageJson.exports).length, 2); +}); + +// Public API contract (exported modules) +test.serial("Package exports: @ui5/linter", async (t) => { + const actual = await import("@ui5/linter"); + const expected = await import("../../lib/index.js"); + t.is(actual, expected, "Correct module exported"); +}); diff --git a/test/lib/cli/base.ts b/test/lib/cli/base.ts index 45fd3a31c..04402f088 100644 --- a/test/lib/cli/base.ts +++ b/test/lib/cli/base.ts @@ -105,8 +105,8 @@ test.serial("ui5lint (default) ", async (t) => { t.true(lintProject.calledOnce, "Linter is called"); t.is(writeFile.callCount, 0, "Coverage was not called"); t.deepEqual(lintProject.getCall(0).args[0], { - rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePattern: undefined, configPath: undefined, - includeMessageDetails: false, reportCoverage: false, ui5ConfigPath: undefined, + rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePatterns: undefined, configPath: undefined, + details: false, coverage: false, ui5Config: undefined, }); t.is(t.context.consoleLogStub.callCount, 0, "console.log should not be used"); }); @@ -119,8 +119,8 @@ test.serial("ui5lint --coverage ", async (t) => { t.true(lintProject.calledOnce, "Linter is called"); t.is(writeFile.callCount, 1, "Coverage was called"); t.deepEqual(lintProject.getCall(0).args[0], { - rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePattern: undefined, configPath: undefined, - includeMessageDetails: false, reportCoverage: true, ui5ConfigPath: undefined, + rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePatterns: undefined, configPath: undefined, + details: false, coverage: true, ui5Config: undefined, }); t.is(t.context.consoleLogStub.callCount, 0, "console.log should not be used"); }); @@ -151,8 +151,8 @@ test.serial("ui5lint --ignore-pattern ", async (t) => { t.true(lintProject.calledOnce, "Linter is called"); t.deepEqual(lintProject.getCall(0).args[0], { - rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePattern: ["test/**/*"], configPath: undefined, - includeMessageDetails: false, reportCoverage: false, ui5ConfigPath: undefined, + rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePatterns: ["test/**/*"], + configPath: undefined, details: false, coverage: false, ui5Config: undefined, }); }); @@ -172,8 +172,8 @@ test.serial("ui5lint --config", async (t) => { t.true(lintProject.calledOnce, "Linter is called"); t.deepEqual(lintProject.getCall(0).args[0], { - rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePattern: undefined, configPath: "config.js", - includeMessageDetails: false, reportCoverage: false, ui5ConfigPath: undefined, + rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePatterns: undefined, configPath: "config.js", + details: false, coverage: false, ui5Config: undefined, }); }); @@ -184,8 +184,8 @@ test.serial("ui5lint --ui5-config", async (t) => { t.true(lintProject.calledOnce, "Linter is called"); t.deepEqual(lintProject.getCall(0).args[0], { - rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePattern: undefined, configPath: undefined, - includeMessageDetails: false, reportCoverage: false, ui5ConfigPath: "ui5.yaml", + rootDir: path.join(process.cwd()), filePatterns: undefined, ignorePatterns: undefined, configPath: undefined, + details: false, coverage: false, ui5Config: "ui5.yaml", }); }); @@ -197,8 +197,8 @@ test.serial("ui5lint path/to/file.js glob/**/*", async (t) => { t.true(lintProject.calledOnce, "Linter is called"); t.deepEqual(lintProject.getCall(0).args[0], { rootDir: path.join(process.cwd()), filePatterns: ["path/to/file.js", "glob/**/*"], - ignorePattern: undefined, configPath: undefined, - includeMessageDetails: false, reportCoverage: false, ui5ConfigPath: undefined, + ignorePatterns: undefined, configPath: undefined, + details: false, coverage: false, ui5Config: undefined, }); }); diff --git a/test/lib/index.ts b/test/lib/index.ts new file mode 100644 index 000000000..8342cad8c --- /dev/null +++ b/test/lib/index.ts @@ -0,0 +1,149 @@ +import anyTest, {TestFn} from "ava"; +import sinonGlobal, {SinonStub} from "sinon"; +import esmock from "esmock"; +import path from "node:path"; +import {fileURLToPath} from "node:url"; +import { + esmockDeprecationText, preprocessLintResultsForSnapshot, +} from "./linter/_linterHelper.js"; +import {LintResult} from "../../src/linter/LinterContext.js"; +import {UI5LinterOptions} from "../../src/index.js"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const fixturesBasePath = path.join(__dirname, "..", "fixtures", "linter"); +const fixturesProjectsPath = path.join(fixturesBasePath, "projects"); + +const test = anyTest as TestFn<{ + sinon: sinonGlobal.SinonSandbox; + ui5lint: SinonStub<[UI5LinterOptions?], Promise>; +}>; + +test.before(async (t) => { + t.context.sinon = sinonGlobal.createSandbox(); + + const {ui5lint} = await esmock("../../src/index.js", { + "../../src/linter/linter.js": await esmockDeprecationText(), + }); + + t.context.ui5lint = ui5lint; +}); +test.after.always((t) => { + t.context.sinon.restore(); +}); + +// Test project fixtures individually +test.serial("ui5lint API: Provide config as an object for com.ui5.troublesome.app", async (t) => { + const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + const {ui5lint} = t.context; + + const res = await ui5lint({ + rootDir: projectPath, + ui5Config: { + specVersion: "3.0", + metadata: { + name: "com.ui5.troublesome.app", + }, + type: "application", + framework: { + name: "OpenUI5", + version: "1.121.0", + libraries: [ + {name: "sap.m"}, + {name: "sap.ui.core"}, + {name: "sap.landvisz"}, + ], + }, + }, + }); + + t.snapshot(preprocessLintResultsForSnapshot(res)); +}); + +test.serial("ui5lint API: Only /webapp folder from com.ui5.troublesome.app", async (t) => { + const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + const filePaths = [ + // Minimatch requires POSIX + "webapp/", + ]; + const {ui5lint} = t.context; + + const res = await ui5lint({ + rootDir: projectPath, + filePatterns: filePaths, + }); + + t.snapshot(preprocessLintResultsForSnapshot(res)); +}); + +test.serial("ui5lint API: com.ui5.troublesome.app with unmatched patterns", async (t) => { + const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + + const {ui5lint} = t.context; + + await t.throwsAsync(ui5lint({ + rootDir: projectPath, + config: "ui5lint.config.unmatched-patterns.mjs", + }), { + message: `Specified file patterns 'unmatched-pattern1', ` + + `'unmatched-pattern2', 'unmatched-pattern3' did not match any resource`, + }); +}); + +test.serial("ui5lint API: Ignore webapp/controller folder from com.ui5.troublesome.app (with details & coverage)", + async (t) => { + const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + const ignorePatterns = [ + // Minimatch requires POSIX + "webapp/controller/", + ]; + const {ui5lint} = t.context; + + const res = await ui5lint({ + rootDir: projectPath, + ignorePatterns, + coverage: true, + details: true, + }); + + t.snapshot(preprocessLintResultsForSnapshot(res)); + }); + +test.serial("ui5lint API: Ignore config file for com.ui5.troublesome.app", async (t) => { + const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + const {ui5lint} = t.context; + + const res = await ui5lint({ + rootDir: projectPath, + noConfig: true, + config: "ui5lint.config.unmatched-patterns.mjs", + }); + + t.snapshot(preprocessLintResultsForSnapshot(res)); +}); + +test.serial("ui5lint API: Use defaults", async (t) => { + const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + const {ui5lint, sinon} = t.context; + // Stub process.cwd(), so we have some app to test + sinon.stub(process, "cwd").returns(projectPath); + + const res = await ui5lint(); + + t.snapshot(preprocessLintResultsForSnapshot(res)); + sinon.restore(); +}); + +test.serial("ui5lint API: Simultaneously test different projects", async (t) => { + const appPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); + const libPath = path.join(fixturesProjectsPath, "library.with.custom.paths"); + const {ui5lint} = t.context; + + const results = await Promise.all([ + ui5lint({rootDir: appPath}), + ui5lint({rootDir: libPath}), + ]); + + results.forEach((res) => { + t.snapshot(preprocessLintResultsForSnapshot(res)); + }); +}); diff --git a/test/lib/linter/_linterHelper.ts b/test/lib/linter/_linterHelper.ts index 52502d269..907c1f9f3 100644 --- a/test/lib/linter/_linterHelper.ts +++ b/test/lib/linter/_linterHelper.ts @@ -143,8 +143,8 @@ function testDefinition( rootDir: fixturesPath, namespace, filePatterns: filePaths, - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); assertExpectedLintResults(t, res, fixturesPath, filePaths.map((fileName) => namespace ? path.join("resources", namespace, fileName) : fileName)); diff --git a/test/lib/linter/linter.ts b/test/lib/linter/linter.ts index 8a9c33f50..45304b677 100644 --- a/test/lib/linter/linter.ts +++ b/test/lib/linter/linter.ts @@ -39,8 +39,8 @@ test.serial("lint: All files of com.ui5.troublesome.app", async (t) => { const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -143,8 +143,8 @@ test.serial("lint: All files of library.with.custom.paths", async (t) => { const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -157,9 +157,9 @@ test.serial("lint: Ignore files from library.with.custom.paths", async (t) => { const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, - ignorePattern: [ + coverage: true, + details: true, + ignorePatterns: [ "src/", "!src/main/", ], @@ -175,8 +175,8 @@ test.serial("lint: All files of library with sap.f namespace", async (t) => { const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -193,8 +193,8 @@ test.serial("lint: All files of mocked minimal sap.ui.core library", async (t) = const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -207,8 +207,8 @@ test.serial("lint: File out of the namespace of sap.ui.core", async (t) => { const res = await lintProject({ rootDir: projectPath, filePatterns: ["src/ui5loader.js"], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -221,8 +221,8 @@ test.serial("lint: All files of library with sap.ui.suite namespace", async (t) const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -235,8 +235,8 @@ test.serial("lint: All files of com.ui5.troublesome.app with custom config", asy const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, + coverage: true, + details: true, configPath: "./ui5lint-custom.config.cjs", }); @@ -250,9 +250,9 @@ test.serial("lint: com.ui5.troublesome.app with custom UI5 config", async (t) => const res = await lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, - ui5ConfigPath: "./configs/ui5-custom.yaml", + coverage: true, + details: true, + ui5Config: "./configs/ui5-custom.yaml", }); t.snapshot(preprocessLintResultsForSnapshot(res)); @@ -261,13 +261,13 @@ test.serial("lint: com.ui5.troublesome.app with custom UI5 config", async (t) => test.serial("lint: com.ui5.troublesome.app with custom UI5 config which does NOT exist", async (t) => { const projectPath = path.join(fixturesProjectsPath, "com.ui5.troublesome.app"); const {lintProject} = t.context; - const ui5ConfigPath = "./configs/ui5-DOES-NOT-EXIST.yaml"; + const ui5Config = "./configs/ui5-DOES-NOT-EXIST.yaml"; await t.throwsAsync(lintProject({ rootDir: projectPath, filePatterns: [], - reportCoverage: true, - includeMessageDetails: true, - ui5ConfigPath, - }), {message: `Unable to find UI5 config file '${ui5ConfigPath}'`}); + coverage: true, + details: true, + ui5Config, + }), {message: `Unable to find UI5 config file '${ui5Config}'`}); }); diff --git a/test/lib/linter/xmlTemplate/_helper.ts b/test/lib/linter/xmlTemplate/_helper.ts index ceb29bf88..bc05b8047 100644 --- a/test/lib/linter/xmlTemplate/_helper.ts +++ b/test/lib/linter/xmlTemplate/_helper.ts @@ -42,7 +42,7 @@ export function createTestsForFixtures(fixturesPath: string) { const fileStream = fs.createReadStream(filePath); const context = new LinterContext({ rootDir: fixturesPath, - includeMessageDetails: true, + details: true, }); const res = await transpileXml(testName, fileStream, context); if (!res) { diff --git a/test/lib/snapshots/index.ts.md b/test/lib/snapshots/index.ts.md new file mode 100644 index 000000000..174db77d5 --- /dev/null +++ b/test/lib/snapshots/index.ts.md @@ -0,0 +1,2424 @@ +# Snapshot report for `test/lib/index.ts` + +The actual snapshot is saved in `index.ts.snap`. + +Generated by [AVA](https://avajs.dev). + +## ui5lint API: Provide config as an object for com.ui5.troublesome.app + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/App.controller.js', + messages: [ + { + column: 36, + line: 1, + message: 'Deprecated access of enum pseudo module \'sap/m/BackgroundDesign\'', + ruleId: 'no-pseudo-modules', + severity: 2, + }, + { + column: 24, + line: 10, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/BaseController.js', + messages: [ + { + column: 5, + line: 9, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 11, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index-cdn.html', + messages: [ + { + column: 4, + line: 15, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 19, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 22, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index.html', + messages: [ + { + column: 4, + line: 14, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'webapp/manifest.json', + messages: [ + { + column: 17, + line: 47, + message: 'Use of deprecated library \'sap.ui.commons\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 13, + line: 59, + message: 'Use of deprecated property \'sap.ui5/resources/js\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 72, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 78, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4-via-dataSource\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 17, + line: 82, + message: 'Use of deprecated class \'sap.ui.model.odata.ODataModel\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.html', + messages: [ + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 3, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.js', + messages: [ + { + column: 18, + line: 4, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 4, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 4, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.html', + messages: [ + { + column: 3, + line: 12, + message: 'Use of unsafe inline script', + ruleId: 'csp-unsafe-inline-script', + severity: 1, + }, + ], + warningCount: 1, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.html', + messages: [ + { + column: 4, + line: 12, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 17, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 9, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.js', + messages: [ + { + column: 18, + line: 6, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 6, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 6, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/view/Main.view.xml', + messages: [ + { + column: 2, + line: 11, + message: 'Import of deprecated module \'sap/m/MessagePage\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 5, + line: 22, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +## ui5lint API: Only /webapp folder from com.ui5.troublesome.app + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/App.controller.js', + messages: [ + { + column: 36, + line: 1, + message: 'Deprecated access of enum pseudo module \'sap/m/BackgroundDesign\'', + ruleId: 'no-pseudo-modules', + severity: 2, + }, + { + column: 24, + line: 10, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/BaseController.js', + messages: [ + { + column: 5, + line: 9, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 11, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index-cdn.html', + messages: [ + { + column: 4, + line: 15, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 19, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 22, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index.html', + messages: [ + { + column: 4, + line: 14, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'webapp/manifest.json', + messages: [ + { + column: 17, + line: 47, + message: 'Use of deprecated library \'sap.ui.commons\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 13, + line: 59, + message: 'Use of deprecated property \'sap.ui5/resources/js\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 72, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 78, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4-via-dataSource\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 17, + line: 82, + message: 'Use of deprecated class \'sap.ui.model.odata.ODataModel\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.html', + messages: [ + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 3, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.js', + messages: [ + { + column: 18, + line: 4, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 4, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 4, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.html', + messages: [ + { + column: 3, + line: 12, + message: 'Use of unsafe inline script', + ruleId: 'csp-unsafe-inline-script', + severity: 1, + }, + ], + warningCount: 1, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.html', + messages: [ + { + column: 4, + line: 12, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 17, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 9, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.js', + messages: [ + { + column: 18, + line: 6, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 6, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 6, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/view/Main.view.xml', + messages: [ + { + column: 2, + line: 11, + message: 'Import of deprecated module \'sap/m/MessagePage\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 5, + line: 22, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +## ui5lint API: Ignore webapp/controller folder from com.ui5.troublesome.app (with details & coverage) + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/Component.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/emptyFile.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/helpers/ES6Class.helper.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index-cdn.html', + messages: [ + { + column: 4, + line: 15, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 19, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 22, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index.html', + messages: [ + { + column: 4, + line: 14, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'webapp/manifest.json', + messages: [ + { + column: 17, + line: 47, + message: 'Use of deprecated library \'sap.ui.commons\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 13, + line: 59, + message: 'Use of deprecated property \'sap.ui5/resources/js\'', + messageDetails: 'As of version 1.94, the usage of js resources is deprecated. Please use regular dependencies instead.', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 72, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4\')', + messageDetails: 'As of version 1.110.0, the \'synchronizationMode\' parameter is obsolete and must be omitted. See the API reference (https://ui5.sap.com/1.120/#/api/sap/ui/model/odata/v4/ODataModel#constructor)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 78, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4-via-dataSource\')', + messageDetails: 'As of version 1.110.0, the \'synchronizationMode\' parameter is obsolete and must be omitted. See the API reference (https://ui5.sap.com/1.120/#/api/sap/ui/model/odata/v4/ODataModel#constructor)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 17, + line: 82, + message: 'Use of deprecated class \'sap.ui.model.odata.ODataModel\'', + messageDetails: 'sap.ui.model.odata.ODataModel (https://ui5.sap.com/1.120/#/api/sap.ui.model.odata.ODataModel)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [ + { + category: 1, + column: 20, + line: 6, + message: 'Unable to analyze this method call because the type of identifier "toUpperCase" in "value.toUpperCase()"" could not be determined', + }, + ], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/model/formatter.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/model/models.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [ + { + category: 1, + column: 2, + line: 6, + message: `Unable to analyze this method call because the type of identifier in "opaTest("Should open the Hello dialog", function (Given, When, Then) {␊ + // Arrangements␊ + Given.iStartMyUIComponent({␊ + componentConfig: {␊ + name: "com.ui5.troublesome.app"␊ + }␊ + });␊ + // Actions␊ + When.onTheMainPage.iPressTheSayHelloButton();␊ + // Assertions␊ + Then.onTheMainPage.iShouldSeeTheHelloDialog();␊ + // Actions␊ + When.onTheMainPage.iPressTheOkButtonInTheDialog();␊ + // Assertions␊ + Then.onTheMainPage.iShouldNotSeeTheHelloDialog();␊ + // Cleanup␊ + Then.iTeardownMyApp();␊ + })"" could not be determined`, + }, + { + category: 1, + column: 3, + line: 8, + message: `Unable to analyze this method call because the type of identifier "iStartMyUIComponent" in "Given.iStartMyUIComponent({␊ + componentConfig: {␊ + name: "com.ui5.troublesome.app"␊ + }␊ + })"" could not be determined`, + }, + { + category: 1, + column: 3, + line: 15, + message: 'Unable to analyze this method call because the type of identifier "iPressTheSayHelloButton" in "When.onTheMainPage.iPressTheSayHelloButton()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 18, + message: 'Unable to analyze this method call because the type of identifier "iShouldSeeTheHelloDialog" in "Then.onTheMainPage.iShouldSeeTheHelloDialog()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 21, + message: 'Unable to analyze this method call because the type of identifier "iPressTheOkButtonInTheDialog" in "When.onTheMainPage.iPressTheOkButtonInTheDialog()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 24, + message: 'Unable to analyze this method call because the type of identifier "iShouldNotSeeTheHelloDialog" in "Then.onTheMainPage.iShouldNotSeeTheHelloDialog()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 27, + message: 'Unable to analyze this method call because the type of identifier "iTeardownMyApp" in "Then.iTeardownMyApp()"" could not be determined', + }, + { + category: 1, + column: 2, + line: 30, + message: `Unable to analyze this method call because the type of identifier in "opaTest("Should close the Hello dialog", function (Given, When, Then) {␊ + // Arrangements␊ + Given.iStartMyUIComponent({␊ + componentConfig: {␊ + name: "com.ui5.troublesome.app"␊ + }␊ + });␊ + // Actions␊ + When.onTheMainPage.iPressTheSayHelloButton();␊ + When.onTheMainPage.iPressTheOkButtonInTheDialog();␊ + // Assertions␊ + Then.onTheMainPage.iShouldNotSeeTheHelloDialog();␊ + // Cleanup␊ + Then.iTeardownMyApp();␊ + })"" could not be determined`, + }, + { + category: 1, + column: 3, + line: 32, + message: `Unable to analyze this method call because the type of identifier "iStartMyUIComponent" in "Given.iStartMyUIComponent({␊ + componentConfig: {␊ + name: "com.ui5.troublesome.app"␊ + }␊ + })"" could not be determined`, + }, + { + category: 1, + column: 3, + line: 39, + message: 'Unable to analyze this method call because the type of identifier "iPressTheSayHelloButton" in "When.onTheMainPage.iPressTheSayHelloButton()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 40, + message: 'Unable to analyze this method call because the type of identifier "iPressTheOkButtonInTheDialog" in "When.onTheMainPage.iPressTheOkButtonInTheDialog()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 43, + message: 'Unable to analyze this method call because the type of identifier "iShouldNotSeeTheHelloDialog" in "Then.onTheMainPage.iShouldNotSeeTheHelloDialog()"" could not be determined', + }, + { + category: 1, + column: 3, + line: 46, + message: 'Unable to analyze this method call because the type of identifier "iTeardownMyApp" in "Then.iTeardownMyApp()"" could not be determined', + }, + ], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/HelloJourney.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.html', + messages: [ + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 3, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.js', + messages: [ + { + column: 18, + line: 4, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + messageDetails: '(since 1.118) - Please use Core.ready (https://ui5.sap.com/1.120/#/api/sap.ui.core.Core) instead.', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 4, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + messageDetails: `(since 1.118) - Please require 'sap/ui/core/Core' instead and use the module export directly␊ + without using 'new'.`, + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 4, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [ + { + category: 1, + column: 13, + line: 8, + message: `Unable to analyze this method call because the type of identifier "waitFor" in "this.waitFor({␊ + id: "helloButton",␊ + viewName: "com.ui5.troublesome.app.view.Main",␊ + actions: new Press(),␊ + errorMessage: "Did not find the 'Say Hello With Dialog' button on the App view"␊ + })"" could not be determined`, + }, + { + category: 1, + column: 13, + line: 17, + message: `Unable to analyze this method call because the type of identifier "waitFor" in "this.waitFor({␊ + controlType: "sap.m.Button",␊ + searchOpenDialogs: true,␊ + viewName: "com.ui5.troublesome.app.view.Main",␊ + actions: new Press(),␊ + errorMessage: "Did not find the 'OK' button in the Dialog"␊ + })"" could not be determined`, + }, + { + category: 1, + column: 13, + line: 29, + message: `Unable to analyze this method call because the type of identifier "waitFor" in "this.waitFor({␊ + controlType: "sap.m.Dialog",␊ + success: function () {␊ + // we set the view busy, so we need to query the parent of the app␊ + Opa5.assert.ok(true, "The dialog is open");␊ + },␊ + errorMessage: "Did not find the dialog control"␊ + })"" could not be determined`, + }, + { + category: 1, + column: 13, + line: 40, + message: `Unable to analyze this method call because the type of identifier "waitFor" in "this.waitFor({␊ + controlType: "sap.m.App", // dummy, I just want a check function, where I can search the DOM. Probably there is a better way for a NEGATIVE test (NO dialog).␊ + check: function () {␊ + return document.querySelectorAll(".sapMDialog").length === 0;␊ + },␊ + success: function () {␊ + Opa5.assert.ok(true, "No dialog is open");␊ + }␊ + })"" could not be determined`, + }, + ], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/pages/Main.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.html', + messages: [ + { + column: 3, + line: 12, + message: 'Use of unsafe inline script', + messageDetails: 'Content Security Policy (https://ui5.sap.com/#/topic/fe1a6dba940e479fb7c3bc753f92b28c)', + ruleId: 'csp-unsafe-inline-script', + severity: 1, + }, + ], + warningCount: 1, + }, + { + coverageInfo: [ + { + category: 1, + column: 2, + line: 6, + message: 'Unable to analyze this method call because the type of identifier "addTestPage" in "suite.addTestPage(sContextPath + "unit/unitTests.qunit.html")"" could not be determined', + }, + { + category: 1, + column: 2, + line: 7, + message: 'Unable to analyze this method call because the type of identifier "addTestPage" in "suite.addTestPage(sContextPath + "integration/opaTests.qunit.html")"" could not be determined', + }, + ], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/controller/App.qunit.js', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.html', + messages: [ + { + column: 4, + line: 12, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 17, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 9, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + messageDetails: 'Compatibility Version Information (deprecated) (https://ui5.sap.com/#/topic/9feb96da02c2429bb1afcf6534d77c79)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.js', + messages: [ + { + column: 18, + line: 6, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + messageDetails: '(since 1.118) - Please use Core.ready (https://ui5.sap.com/1.120/#/api/sap.ui.core.Core) instead.', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 6, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + messageDetails: `(since 1.118) - Please require 'sap/ui/core/Core' instead and use the module export directly␊ + without using 'new'.`, + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 6, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + messageDetails: 'Do not use global variables to access UI5 modules or APIs. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/view/App.view.xml', + messages: [], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/view/Main.view.xml', + messages: [ + { + column: 2, + line: 11, + message: 'Import of deprecated module \'sap/m/MessagePage\'', + messageDetails: '(since 1.112) - Use the sap.m.IllustratedMessage (https://ui5.sap.com/1.120/#/api/sap.m.IllustratedMessage) instead.', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 5, + line: 22, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + messageDetails: `(since 1.69) - The blocked property is deprecated. There is no accessibility support for␊ + this property. Blocked controls should not be used inside Controls, which rely on keyboard navigation,␊ + e.g. List controls.`, + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +## ui5lint API: Ignore config file for com.ui5.troublesome.app + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'ui5.yaml', + messages: [ + { + column: 7, + line: 11, + message: 'Use of deprecated library \'sap.landvisz\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/App.controller.js', + messages: [ + { + column: 36, + line: 1, + message: 'Deprecated access of enum pseudo module \'sap/m/BackgroundDesign\'', + ruleId: 'no-pseudo-modules', + severity: 2, + }, + { + column: 24, + line: 10, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/BaseController.js', + messages: [ + { + column: 5, + line: 9, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 11, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index-cdn.html', + messages: [ + { + column: 4, + line: 15, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 19, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 22, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index.html', + messages: [ + { + column: 4, + line: 14, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'webapp/manifest.json', + messages: [ + { + column: 17, + line: 47, + message: 'Use of deprecated library \'sap.ui.commons\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 13, + line: 59, + message: 'Use of deprecated property \'sap.ui5/resources/js\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 72, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 78, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4-via-dataSource\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 17, + line: 82, + message: 'Use of deprecated class \'sap.ui.model.odata.ODataModel\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.html', + messages: [ + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 3, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.js', + messages: [ + { + column: 18, + line: 4, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 4, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 4, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.html', + messages: [ + { + column: 3, + line: 12, + message: 'Use of unsafe inline script', + ruleId: 'csp-unsafe-inline-script', + severity: 1, + }, + ], + warningCount: 1, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.html', + messages: [ + { + column: 4, + line: 12, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 17, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 9, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.js', + messages: [ + { + column: 18, + line: 6, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 6, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 6, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/view/Main.view.xml', + messages: [ + { + column: 2, + line: 11, + message: 'Import of deprecated module \'sap/m/MessagePage\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 5, + line: 22, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +## ui5lint API: Use defaults + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/App.controller.js', + messages: [ + { + column: 36, + line: 1, + message: 'Deprecated access of enum pseudo module \'sap/m/BackgroundDesign\'', + ruleId: 'no-pseudo-modules', + severity: 2, + }, + { + column: 24, + line: 10, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/BaseController.js', + messages: [ + { + column: 5, + line: 9, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 11, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index-cdn.html', + messages: [ + { + column: 4, + line: 15, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 19, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 22, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index.html', + messages: [ + { + column: 4, + line: 14, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'webapp/manifest.json', + messages: [ + { + column: 17, + line: 47, + message: 'Use of deprecated library \'sap.ui.commons\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 13, + line: 59, + message: 'Use of deprecated property \'sap.ui5/resources/js\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 72, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 78, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4-via-dataSource\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 17, + line: 82, + message: 'Use of deprecated class \'sap.ui.model.odata.ODataModel\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.html', + messages: [ + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 3, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.js', + messages: [ + { + column: 18, + line: 4, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 4, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 4, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.html', + messages: [ + { + column: 3, + line: 12, + message: 'Use of unsafe inline script', + ruleId: 'csp-unsafe-inline-script', + severity: 1, + }, + ], + warningCount: 1, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.html', + messages: [ + { + column: 4, + line: 12, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 17, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 9, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.js', + messages: [ + { + column: 18, + line: 6, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 6, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 6, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/view/Main.view.xml', + messages: [ + { + column: 2, + line: 11, + message: 'Import of deprecated module \'sap/m/MessagePage\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 5, + line: 22, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +## ui5lint API: Simultaneously test different projects + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/App.controller.js', + messages: [ + { + column: 36, + line: 1, + message: 'Deprecated access of enum pseudo module \'sap/m/BackgroundDesign\'', + ruleId: 'no-pseudo-modules', + severity: 2, + }, + { + column: 24, + line: 10, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/controller/BaseController.js', + messages: [ + { + column: 5, + line: 9, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 11, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index-cdn.html', + messages: [ + { + column: 4, + line: 15, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 19, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 22, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/index.html', + messages: [ + { + column: 4, + line: 14, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 21, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-frameOptions\'; should be written as \'data-sap-ui-frame-options\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 4, + }, + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'webapp/manifest.json', + messages: [ + { + column: 17, + line: 47, + message: 'Use of deprecated library \'sap.ui.commons\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 13, + line: 59, + message: 'Use of deprecated property \'sap.ui5/resources/js\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 72, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 21, + line: 78, + message: 'Usage of deprecated parameter \'synchronizationMode\' of constructor \'sap/ui/model/odata/v4/ODataModel\' (model: \'odata-v4-via-dataSource\')', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 17, + line: 82, + message: 'Use of deprecated class \'sap.ui.model.odata.ODataModel\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.html', + messages: [ + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 18, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-compatVersion\'; should be written as \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 20, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + ], + warningCount: 3, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/integration/opaTests.qunit.js', + messages: [ + { + column: 18, + line: 4, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 4, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 4, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'webapp/test/testsuite.qunit.html', + messages: [ + { + column: 3, + line: 12, + message: 'Use of unsafe inline script', + ruleId: 'csp-unsafe-inline-script', + severity: 1, + }, + ], + warningCount: 1, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.html', + messages: [ + { + column: 4, + line: 12, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 17, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 9, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'webapp/test/unit/unitTests.qunit.js', + messages: [ + { + column: 18, + line: 6, + message: 'Call to deprecated function \'attachInit\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 6, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 6, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'webapp/view/Main.view.xml', + messages: [ + { + column: 2, + line: 11, + message: 'Import of deprecated module \'sap/m/MessagePage\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 5, + line: 22, + message: 'Use of deprecated property \'blocked\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +> Snapshot 2 + + [ + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'src/main/js/.library', + messages: [ + { + column: 4, + line: 25, + message: 'Use of deprecated library \'sap.ca.scfld.md\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 4, + line: 28, + message: 'Use of deprecated library \'sap.ca.scfld.md\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + { + column: 4, + line: 31, + message: 'Use of deprecated library \'sap.ca.ui\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'src/main/js/Example.js', + messages: [ + { + column: 10, + line: 64, + message: 'Call to deprecated function \'rerender\' (this.rerender)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'src/main/js/ExampleRenderer.js', + messages: [ + { + column: 19, + line: 27, + message: 'Call to deprecated function \'getLibraryResourceBundle\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 3, + fatalErrorCount: 0, + filePath: 'src/main/js/library.js', + messages: [ + { + column: 19, + line: 15, + message: 'Call to deprecated function \'initLibrary\' of class \'Core\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 9, + line: 15, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 2, + line: 15, + message: 'Access of global variable \'sap\' (sap.ui.getCore)', + ruleId: 'no-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'src/test/js/ButtonFactory.js', + messages: [ + { + column: 31, + line: 4, + message: 'Call to deprecated function \'attachTap\' of class \'Button\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'src/test/js/Example.html', + messages: [ + { + column: 4, + line: 11, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-oninit\'; should be written as \'data-sap-ui-on-init\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 4, + line: 13, + message: 'Outdated spelling of bootstrap parameter: \'data-sap-ui-resourceroots\'; should be written as \'data-sap-ui-resource-roots\'', + ruleId: 'no-deprecated-api', + severity: 1, + }, + { + column: 3, + line: 7, + message: 'Missing bootstrap parameter \'data-sap-ui-compat-version\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 2, + }, + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: 'src/test/js/Example.js', + messages: [ + { + column: 90, + line: 1, + message: 'Import of deprecated module \'sap/ui/model/odata/ODataModel\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 25, + line: 19, + message: 'Call to deprecated function \'getBlocked\' of class \'Example\'', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + { + coverageInfo: [], + errorCount: 1, + fatalErrorCount: 0, + filePath: 'ui5.yaml', + messages: [ + { + column: 7, + line: 15, + message: 'Use of deprecated library \'sap.landvisz\'', + ruleId: 'no-deprecated-library', + severity: 2, + }, + ], + warningCount: 0, + }, + ] diff --git a/test/lib/snapshots/index.ts.snap b/test/lib/snapshots/index.ts.snap new file mode 100644 index 0000000000000000000000000000000000000000..d070137d116d0206791d2337bca8bf8aa3fe9669 GIT binary patch literal 12269 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