diff --git a/src/linter/binding/BindingLinter.ts b/src/linter/binding/BindingLinter.ts index 64b7ac50f..f068fb82d 100644 --- a/src/linter/binding/BindingLinter.ts +++ b/src/linter/binding/BindingLinter.ts @@ -2,9 +2,16 @@ import LinterContext, {PositionInfo, ResourcePath} from "../LinterContext.js"; import {MESSAGE} from "../messages.js"; import {RequireDeclaration} from "../xmlTemplate/Parser.js"; import BindingParser, { - AggregationBindingInfo, BindingInfo, FilterInfo, PropertyBindingInfo, SorterInfo, + AggregationBindingInfo, BindingInfo, ExpressionBinding, FilterInfo, PropertyBindingInfo, SorterInfo, } from "./lib/BindingParser.js"; +const ODATA_EXPRESSION_ADDONS_MODULE = "sap/ui/model/odata/ODataExpressionAddons"; +const ODATA_FUNCTION_MODULE_MAP: Record = { + compare: "sap/ui/model/odata/v4/ODataUtils", + fillUriTemplate: "sap/ui/thirdparty/URITemplate", + uriEncode: "sap/ui/model/odata/ODataUtils", +} as const; + export default class BindingLinter { #resourcePath: string; #context: LinterContext; @@ -14,8 +21,9 @@ export default class BindingLinter { this.#context = context; } - #parseBinding(binding: string): BindingInfo { - const bindingInfo = BindingParser.complexParser(binding, null, true, true, true, true); + #parseBinding(binding: string): BindingInfo | string | undefined { + // Do not unescape (3rd argument), as we're only interested in the binding + const bindingInfo = BindingParser.complexParser(binding, null, false, true, true, true); return bindingInfo; } @@ -131,8 +139,11 @@ export default class BindingLinter { lintPropertyBinding(bindingDefinition: string, requireDeclarations: RequireDeclaration[], position: PositionInfo) { try { const bindingInfo = this.#parseBinding(bindingDefinition); - if (bindingInfo) { + if (typeof bindingInfo === "object") { this.#analyzePropertyBinding(bindingInfo as PropertyBindingInfo, requireDeclarations, position); + if ("tokens" in bindingInfo) { + this.#lintExpressionBindingTokens(bindingInfo, requireDeclarations, position); + } } } catch (err) { const message = err instanceof Error ? err.message : String(err); @@ -144,7 +155,7 @@ export default class BindingLinter { bindingDefinition: string, requireDeclarations: RequireDeclaration[], position: PositionInfo) { try { const bindingInfo = this.#parseBinding(bindingDefinition); - if (bindingInfo) { + if (typeof bindingInfo === "object") { this.#analyzeAggregationBinding(bindingInfo as AggregationBindingInfo, requireDeclarations, position); } } catch (err) { @@ -153,27 +164,35 @@ export default class BindingLinter { } } - #isExpressionBinding(bindingDefinition: string): boolean { - return /^\{:?=/.test(bindingDefinition) && bindingDefinition.endsWith("}"); - } - - lintPropertyExpression( - bindingDefinition: string, requireDeclarations: RequireDeclaration[], position: PositionInfo) { - if (!this.#isExpressionBinding(bindingDefinition)) { - return; - } - const allFunctionsModule = "sap/ui/model/odata/ODataExpressionAddons"; - const varModuleMap = { - "odata.compare": "sap/ui/model/odata/v4/ODataUtils", - "odata.fillUriTemplate": "sap/ui/thirdparty/URITemplate", - "odata.uriEncode": "sap/ui/model/odata/ODataUtils", - }; - - for (const [key, value] of Object.entries(varModuleMap)) { - if (bindingDefinition.includes(`${key}(`) && - !requireDeclarations.some((decl) => decl.moduleName === value || value === allFunctionsModule)) { - this.#context.addLintingMessage(this.#resourcePath, MESSAGE.NO_ODATA_GLOBALS, {} as never, position); + #lintExpressionBindingTokens( + expressionBinding: ExpressionBinding, requireDeclarations: RequireDeclaration[], position: PositionInfo + ) { + const {tokens} = expressionBinding; + for (let i = 0; i < tokens.length; i++) { + const token = tokens[i]; + if (token.id !== "IDENTIFIER" || token.value !== "odata" || tokens[i + 1]?.id !== ".") { + continue; + } + const functionToken = tokens[i + 2]; + if (functionToken?.id !== "IDENTIFIER") { + // Can't happen. A "." must be followed by an IDENTIFIER + continue; + } + const functionName = functionToken.value; + if (functionName in ODATA_FUNCTION_MODULE_MAP) { + const expectedModuleName = ODATA_FUNCTION_MODULE_MAP[functionName]; + if ( + // There must be either an import for the corresponding module + // or for the ODataExpressionAddons module + !requireDeclarations.some((decl) => + decl.moduleName === expectedModuleName || decl.moduleName === ODATA_EXPRESSION_ADDONS_MODULE) + ) { + this.#context.addLintingMessage( + this.#resourcePath, MESSAGE.NO_ODATA_GLOBALS, {} as never, position + ); + } } + i += 2; // Skip the next two tokens as we already checked them } } } diff --git a/src/linter/binding/lib/BindingParser.d.ts b/src/linter/binding/lib/BindingParser.d.ts index ad6ee27ec..8154edd49 100644 --- a/src/linter/binding/lib/BindingParser.d.ts +++ b/src/linter/binding/lib/BindingParser.d.ts @@ -33,7 +33,16 @@ export interface AggregationBindingInfo extends BindingInfoBase { groupHeaderFactory?: FunctionReference; } -export type BindingInfo = PropertyBindingInfo | AggregationBindingInfo; +interface ExpressionBindingToken { + id: "IDENTIFIER" | "."; // Only the ones required for UI5 linter + value: string; +} + +export interface ExpressionBinding extends BindingInfoBase { + tokens: ExpressionBindingToken[]; +} + +export type BindingInfo = PropertyBindingInfo | AggregationBindingInfo | ExpressionBinding; interface BindingParser { complexParser: ( @@ -45,7 +54,7 @@ interface BindingParser { bPreferContext?: boolean, mLocals?: Record, bResolveTypesAsync?: boolean - ) => BindingInfo; + ) => BindingInfo | string | undefined; // Might return a string when bUnescape is true } declare const BindingParser: BindingParser; diff --git a/src/linter/binding/lib/ExpressionParser.js b/src/linter/binding/lib/ExpressionParser.js index e81e7a4a9..2ac33d676 100644 --- a/src/linter/binding/lib/ExpressionParser.js +++ b/src/linter/binding/lib/ExpressionParser.js @@ -929,7 +929,9 @@ export default { /* UI5 LINTER MODIFICATION: Disabled the next few formatter-related lines. - The ExpressionParser formatter function is not required for linting purposes + The ExpressionParser formatter function is not required for linting purposes. + + Instead, the tokens are returned to allow linting the individual parts of the expression. */ // function formatter() { // //turn separate parameters for parts into one (array like) parameter @@ -939,7 +941,8 @@ export default { return { result: { // formatter: formatter, - parts: oTokens.parts + parts: oTokens.parts, + tokens: oTokens.tokens }, at: oResult.at || oTokens.at }; diff --git a/src/linter/xmlTemplate/Parser.ts b/src/linter/xmlTemplate/Parser.ts index f7d9c26d5..ad0acf0f3 100644 --- a/src/linter/xmlTemplate/Parser.ts +++ b/src/linter/xmlTemplate/Parser.ts @@ -561,10 +561,6 @@ export default class Parser { line: prop.start.line + 1, // Add one to align with IDEs column: prop.start.column + 1, }); - this.#bindingLinter.lintPropertyExpression(prop.value, this.#requireDeclarations, { - line: prop.start.line + 1, // Add one to align with IDEs - column: prop.start.column + 1, - }); } else if (this.#apiExtract.isAggregation(`${namespace}.${moduleName}`, prop.name)) { this.#bindingLinter.lintAggregationBinding(prop.value, this.#requireDeclarations, { line: prop.start.line + 1, // Add one to align with IDEs diff --git a/test/fixtures/linter/rules/NoGlobals/NoImplicitGlobalsOData.js b/test/fixtures/linter/rules/NoGlobals/NoImplicitGlobalsOData.js new file mode 100644 index 000000000..55990a26b --- /dev/null +++ b/test/fixtures/linter/rules/NoGlobals/NoImplicitGlobalsOData.js @@ -0,0 +1,11 @@ +sap.ui.define(["sap/m/Text"], (Text) => { + const oText1 = new Text({ + text: "{= odata.compare(%{myvalue1},%{myvalue2})}" + }); + const oText2 = new Text({ + text: "{= odata.fillUriTemplate(${myvalue1},${myvalue2})}" + }); + oText2.applySettings({ + text: "{= odata.uriEncode(%{myvalue1},'Edm.String') + ' - ' + odata.uriEncode(%{myvalue2},'Edm.String') }" + }); +}); diff --git a/test/fixtures/linter/rules/NoGlobals/NoImplicitGlobalsOData_Negative.js b/test/fixtures/linter/rules/NoGlobals/NoImplicitGlobalsOData_Negative.js new file mode 100644 index 000000000..c3396f5b9 --- /dev/null +++ b/test/fixtures/linter/rules/NoGlobals/NoImplicitGlobalsOData_Negative.js @@ -0,0 +1,14 @@ +sap.ui.define(["sap/m/Text", "sap/ui/model/odata/ODataExpressionAddons"], (Text) => { + const oText1 = new Text({ + text: "{= odata.compare(%{myvalue1},%{myvalue2})}" + }); + const oText2 = new Text({ + text: "{= odata.fillUriTemplate(${myvalue1},${myvalue2})}" + }); + oText2.applySettings({ + text: "{= odata.uriEncode(%{myvalue1},'Edm.String') + ' - ' + odata.uriEncode(%{myvalue2},'Edm.String') }" + }); + oText2.applySettings({ + text: `\\{"foo": "bar"}` // Escaped JSON string should not cause issues when checking for odata globals within a binding + }); +}); diff --git a/test/lib/linter/binding/BindingLinter.ts b/test/lib/linter/binding/BindingLinter.ts index 21f480f40..1344cb1ec 100644 --- a/test/lib/linter/binding/BindingLinter.ts +++ b/test/lib/linter/binding/BindingLinter.ts @@ -347,6 +347,36 @@ test("XML Expression Binding with encoded ampersand", (t) => { t.snapshot(linterContext.generateLintResult("/test.js")); }); +test("XML Expression Binding with odata function calls", (t) => { + const {bindingLinter, linterContext} = t.context; + + bindingLinter.lintPropertyBinding( + "{= odata.uriEncode(%{myvalue1},'Edm.String') + ' - ' + odata.uriEncode(%{myvalue2},'Edm.String') }", + [], {line: 1, column: 1}); + + t.snapshot(linterContext.generateLintResult("/test.js")); +}); + +test("XML Expression Binding with unknown odata function call", (t) => { + const {bindingLinter, linterContext} = t.context; + + bindingLinter.lintPropertyBinding( + "{= odata.foo(%{myvalue1},'Edm.String') }", + [], {line: 1, column: 1}); + + t.snapshot(linterContext.generateLintResult("/test.js")); +}); + +test("XML Expression Binding with nested unknown odata function call", (t) => { + const {bindingLinter, linterContext} = t.context; + + bindingLinter.lintPropertyBinding( + "{= odata.foo.bar(%{myvalue1},'Edm.String') }", + [], {line: 1, column: 1}); + + t.snapshot(linterContext.generateLintResult("/test.js")); +}); + test("Error Testing: XML Property Binding missing closing bracket", (t) => { const {bindingLinter, linterContext} = t.context; @@ -356,3 +386,23 @@ test("Error Testing: XML Property Binding missing closing bracket", (t) => { t.snapshot(linterContext.generateLintResult("/test.js")); }); + +test("Error Testing: Escaped JSON (escaped opening bracket)", (t) => { + const {bindingLinter, linterContext} = t.context; + + bindingLinter.lintPropertyBinding( + `\\{"foo": "bar"}`, + [], {line: 1, column: 1}); + + t.snapshot(linterContext.generateLintResult("/test.js")); +}); + +test("Error Testing: Escaped JSON (escaped opening/closing brackets)", (t) => { + const {bindingLinter, linterContext} = t.context; + + bindingLinter.lintPropertyBinding( + `\\{"foo": "bar"\\}`, + [], {line: 1, column: 1}); + + t.snapshot(linterContext.generateLintResult("/test.js")); +}); diff --git a/test/lib/linter/binding/lib/BindingParser.ts b/test/lib/linter/binding/lib/BindingParser.ts index 1dae5a002..942ea6484 100644 --- a/test/lib/linter/binding/lib/BindingParser.ts +++ b/test/lib/linter/binding/lib/BindingParser.ts @@ -2,12 +2,12 @@ import anyTest, {TestFn} from "ava"; import BindingParser, {BindingInfo} from "../../../../../src/linter/binding/lib/BindingParser.js"; const test = anyTest as TestFn<{ - parse: (string: string) => BindingInfo; + parse: (string: string) => BindingInfo | string | undefined; }>; test.before((t) => { t.context.parse = function (string: string) { - return BindingParser.complexParser(string, null, true, true, true, true); + return BindingParser.complexParser(string, null, false, true, true, true); }; }); @@ -108,7 +108,7 @@ test("XML Binding: Expression Binding with an embedded composite binding", (t) = const {parse} = t.context; const res = parse(`{= %{/data/message}.length < 20 - ? %{i18n>errorMsg} + ? %{i18n>errorMsg} : %{parts: [ {path: 'i18n>successMsg'}, {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}, @@ -116,3 +116,29 @@ test("XML Binding: Expression Binding with an embedded composite binding", (t) = ], formatter: 'my.globalFormatter'}}`); t.snapshot(res); }); + +test("No binding: Just text", (t) => { + const {parse} = t.context; + + const res = parse(`foo bar`); + t.snapshot(res); +}); + +test("No binding: Escaped JSON (escaped opening bracket)", (t) => { + const {parse} = t.context; + + const res = parse(`\\{"foo": "bar"}`); + t.snapshot(res); +}); + +test("No binding: Escaped JSON (escaped opening/closing brackets)", (t) => { + const {parse} = t.context; + + const res = parse(`\\{"foo": "bar"\\}`); + t.snapshot(res); +}); + +test("No binding: Escaped JSON (escaped opening/closing brackets), bUnescape=true", (t) => { + const res = BindingParser.complexParser(`\\{"foo": "bar"\\}`, null, /* bUnescape */ true, true, true, true); + t.snapshot(res); +}); diff --git a/test/lib/linter/binding/lib/snapshots/BindingParser.ts.md b/test/lib/linter/binding/lib/snapshots/BindingParser.ts.md index 34a960152..363289997 100644 --- a/test/lib/linter/binding/lib/snapshots/BindingParser.ts.md +++ b/test/lib/linter/binding/lib/snapshots/BindingParser.ts.md @@ -120,6 +120,34 @@ Generated by [AVA](https://avajs.dev). targetType: 'any', }, ], + tokens: [ + { + end: 12, + id: 'BINDING', + input: '{= %{status} === \'critical\' }', + start: 3, + value: 0, + }, + { + end: 16, + id: '===', + input: '{= %{status} === \'critical\' }', + start: 13, + }, + { + end: 27, + id: 'CONSTANT', + input: '{= %{status} === \'critical\' }', + start: 17, + value: 'critical', + }, + { + end: 29, + id: '}', + input: '{= %{status} === \'critical\' }', + start: 28, + }, + ], } ## XML Binding: Expression Binding with an embedded composite binding @@ -171,4 +199,155 @@ Generated by [AVA](https://avajs.dev). ], }, ], + tokens: [ + { + end: 19, + id: 'BINDING', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 3, + value: 0, + }, + { + end: 20, + id: '.', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 19, + }, + { + end: 26, + id: 'IDENTIFIER', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 20, + value: 'length', + }, + { + end: 28, + id: '<', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 27, + }, + { + end: 31, + id: 'CONSTANT', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 29, + value: 20, + }, + { + end: 39, + id: '?', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 38, + }, + { + end: 56, + id: 'BINDING', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 40, + value: 1, + }, + { + end: 64, + id: ':', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 63, + }, + { + end: 353, + id: 'BINDING', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 65, + value: 2, + }, + { + end: 354, + id: '}', + input: `{= %{/data/message}.length < 20␊ + ? %{i18n>errorMsg}␊ + : %{parts: [␊ + {path: 'i18n>successMsg'},␊ + {path: '/data/today', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}},␊ + {path: '/data/tomorrow', type:'sap.ui.model.type.Date', constraints:{displayFormat:'Date'}}␊ + ], formatter: 'my.globalFormatter'}}`, + start: 353, + }, + ], } + +## No binding: Just text + +> Snapshot 1 + + undefined + +## No binding: Escaped JSON (escaped opening bracket) + +> Snapshot 1 + + undefined + +## No binding: Escaped JSON (escaped opening/closing brackets) + +> Snapshot 1 + + undefined + +## No binding: Escaped JSON (escaped opening/closing brackets), bUnescape=true + +> Snapshot 1 + + '{"foo": "bar"}' diff --git a/test/lib/linter/binding/lib/snapshots/BindingParser.ts.snap b/test/lib/linter/binding/lib/snapshots/BindingParser.ts.snap index 1c616c85c..e3beeb097 100644 Binary files a/test/lib/linter/binding/lib/snapshots/BindingParser.ts.snap and b/test/lib/linter/binding/lib/snapshots/BindingParser.ts.snap differ diff --git a/test/lib/linter/binding/snapshots/BindingLinter.ts.md b/test/lib/linter/binding/snapshots/BindingLinter.ts.md index 31cdcd9a6..ed321887c 100644 --- a/test/lib/linter/binding/snapshots/BindingLinter.ts.md +++ b/test/lib/linter/binding/snapshots/BindingLinter.ts.md @@ -398,6 +398,60 @@ Generated by [AVA](https://avajs.dev). ## XML Expression Binding with encoded ampersand +> Snapshot 1 + + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: '/test.js', + messages: [], + warningCount: 0, + } + +## XML Expression Binding with odata function calls + +> Snapshot 1 + + { + coverageInfo: [], + errorCount: 2, + fatalErrorCount: 0, + filePath: '/test.js', + messages: [ + { + column: 1, + line: 1, + message: 'OData built-in global symbols must not be used implicitly', + ruleId: 'no-implicit-globals', + severity: 2, + }, + { + column: 1, + line: 1, + message: 'OData built-in global symbols must not be used implicitly', + ruleId: 'no-implicit-globals', + severity: 2, + }, + ], + warningCount: 0, + } + +## XML Expression Binding with unknown odata function call + +> Snapshot 1 + + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: '/test.js', + messages: [], + warningCount: 0, + } + +## XML Expression Binding with nested unknown odata function call + > Snapshot 1 { @@ -430,3 +484,29 @@ Generated by [AVA](https://avajs.dev). ], warningCount: 0, } + +## Error Testing: Escaped JSON (escaped opening bracket) + +> Snapshot 1 + + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: '/test.js', + messages: [], + warningCount: 0, + } + +## Error Testing: Escaped JSON (escaped opening/closing brackets) + +> Snapshot 1 + + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: '/test.js', + messages: [], + warningCount: 0, + } diff --git a/test/lib/linter/binding/snapshots/BindingLinter.ts.snap b/test/lib/linter/binding/snapshots/BindingLinter.ts.snap index 2f5267bc1..f4d0597cb 100644 Binary files a/test/lib/linter/binding/snapshots/BindingLinter.ts.snap and b/test/lib/linter/binding/snapshots/BindingLinter.ts.snap differ diff --git a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md index 95f3b7c7b..9bb966508 100644 --- a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md +++ b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.md @@ -1689,6 +1689,62 @@ Generated by [AVA](https://avajs.dev). }, ] +## General: SapUiGetCore.js + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 5, + fatalErrorCount: 0, + filePath: 'SapUiGetCore.js', + messages: [ + { + column: 14, + line: 2, + 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, + }, + { + column: 21, + line: 2, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + messageDetails: 'Deprecated test message', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 6, + line: 5, + message: 'Call to deprecated function \'getModel\' of class \'Core\'', + messageDetails: 'Deprecated test message', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 1, + line: 9, + message: 'Use of deprecated property \'getModel\' (sap.ui.getCore().getModel)', + messageDetails: 'Deprecated test message', + ruleId: 'no-deprecated-api', + severity: 2, + }, + { + column: 8, + line: 9, + message: 'Call to deprecated function \'getCore\' (sap.ui.getCore)', + messageDetails: 'Deprecated test message', + ruleId: 'no-deprecated-api', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + ## General: XMLFragment.fragment.xml > Snapshot 1 diff --git a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap index e1eeadda7..223233e79 100644 Binary files a/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap and b/test/lib/linter/rules/snapshots/NoDeprecatedApi.ts.snap differ diff --git a/test/lib/linter/rules/snapshots/NoGlobals.ts.md b/test/lib/linter/rules/snapshots/NoGlobals.ts.md index 4ab2e0e95..bb7c4cf3a 100644 --- a/test/lib/linter/rules/snapshots/NoGlobals.ts.md +++ b/test/lib/linter/rules/snapshots/NoGlobals.ts.md @@ -599,3 +599,66 @@ Generated by [AVA](https://avajs.dev). warningCount: 0, }, ] + +## General: NoImplicitGlobalsOData.js + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 4, + fatalErrorCount: 0, + filePath: 'NoImplicitGlobalsOData.js', + messages: [ + { + column: 3, + line: 3, + message: 'OData built-in global symbols must not be used implicitly', + messageDetails: 'Import the \'sap/ui/model/odata/ODataExpressionAddons\' module instead. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-implicit-globals', + severity: 2, + }, + { + column: 3, + line: 6, + message: 'OData built-in global symbols must not be used implicitly', + messageDetails: 'Import the \'sap/ui/model/odata/ODataExpressionAddons\' module instead. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-implicit-globals', + severity: 2, + }, + { + column: 3, + line: 9, + message: 'OData built-in global symbols must not be used implicitly', + messageDetails: 'Import the \'sap/ui/model/odata/ODataExpressionAddons\' module instead. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-implicit-globals', + severity: 2, + }, + { + column: 3, + line: 9, + message: 'OData built-in global symbols must not be used implicitly', + messageDetails: 'Import the \'sap/ui/model/odata/ODataExpressionAddons\' module instead. See Best Practices for Developers (https://ui5.sap.com/#/topic/28fcd55b04654977b63dacbee0552712)', + ruleId: 'no-implicit-globals', + severity: 2, + }, + ], + warningCount: 0, + }, + ] + +## General: NoImplicitGlobalsOData_Negative.js + +> Snapshot 1 + + [ + { + coverageInfo: [], + errorCount: 0, + fatalErrorCount: 0, + filePath: 'NoImplicitGlobalsOData_Negative.js', + messages: [], + warningCount: 0, + }, + ] diff --git a/test/lib/linter/rules/snapshots/NoGlobals.ts.snap b/test/lib/linter/rules/snapshots/NoGlobals.ts.snap index f98cde5be..028368303 100644 Binary files a/test/lib/linter/rules/snapshots/NoGlobals.ts.snap and b/test/lib/linter/rules/snapshots/NoGlobals.ts.snap differ