@vlocode/apex
Version:
Salesforce APEX Parser and Grammar
332 lines (330 loc) • 13.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
require("jest");
const parser_1 = require("../parser");
describe('ApexParser', () => {
describe('#getCodeStructure', () => {
it('should parse local variable declarations', () => {
// Arrange
const code = `
public class MyClass {
public void myMethod() {
String localVar1 = null;
String localVar2 = null;
if (localVar1 == localVar2) {
MyClass nested = new MyClass();
} else {
OtherClass nested = new OtherClass();
}
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myMethod] = actualCodeStructure.classes[0].methods;
const [ifBlock, elseBlock] = myMethod.blocks;
expect(myMethod.localVariables.sort()).toMatchObject([
{ name: 'localVar1', type: { name: 'String', isSystemType: true } },
{ name: 'localVar2', type: { name: 'String', isSystemType: true } }
]);
expect(ifBlock.localVariables).toMatchObject([
{ name: 'nested', type: { name: 'MyClass' } }
]);
expect(elseBlock.localVariables).toMatchObject([
{ name: 'nested', type: { name: 'OtherClass' } }
]);
expect([...new Set(myMethod.refs.map(r => r.name))].sort()).toEqual([
'MyClass', 'OtherClass', 'String'
]);
});
it('should parse class variable declarations', () => {
// Arrange
const code = `
public class MyClass {
PRIVATE String classLocal1 = null;
protected String classLocal2 = null;
final MyClass nested = new MyClass();
protected static String staticValue = null;
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
expect(myClass.fields.sort()).toMatchObject([
{ name: 'classLocal1', access: 'private', type: { name: 'String' } },
{ name: 'classLocal2', access: 'protected', type: { name: 'String' } },
{ name: 'nested', isFinal: true, type: { name: 'MyClass' } },
{ name: 'staticValue', isStatic: true, access: 'protected', type: { name: 'String' } },
]);
expect(myClass.refs.map(r => r.name).sort()).toEqual(['MyClass', 'String']);
});
it('should parse implemented interfaces', () => {
// Arrange
const code = `
public class MyClass implements Interface1, Interface2 {
public void myMethod() {
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
expect(myClass.implements.map(r => r.name).sort()).toEqual(['Interface1', 'Interface2']);
});
it('should parse properties', () => {
// Arrange
const code = `
public class MyClass {
public String myProperty1 { get; set; } // Auto generated backing fields
public String myProperty2 {
get {
return null;
}
} // Readonly property
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [myProperty1, myProperty2] = myClass.properties.sort();
expect(myProperty1).toMatchObject({
name: 'myProperty1',
access: 'public',
type: {
name: 'String',
isSystemType: true
},
sourceRange: {
start: { line: 4, column: 21 },
stop: { line: 4, column: 60 }
}
});
expect(myProperty2).toMatchObject({
name: 'myProperty2',
access: 'public',
type: {
name: 'String',
isSystemType: true
},
sourceRange: {
start: { line: 6, column: 21 },
stop: { line: 10, column: 22 }
}
});
});
it('should extract static member access as references', () => {
// Arrange
const code = `
public class MyClass {
public void myMethod() {
String methodLocal = null;
ExternalClass1.externalMethod1();
this.localMethod2();
ExternalClass3.variable.method();
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [myMethod] = actualCodeStructure.classes[0].methods;
expect(myMethod.refs.sort()).toMatchObject([
{ name: 'String', isSystemType: true },
{ name: 'ExternalClass1', isSystemType: false },
{ name: 'ExternalClass3', isSystemType: false }
]);
expect(myClass.refs.map(r => r.name).sort()).toEqual([
'ExternalClass1', 'ExternalClass3', 'String'
]);
});
it('should extract constructed types as references', () => {
// Arrange
const code = `
public class MyClass {
public void myMethod() {
for(Object a : new ExternalClass1().getThings()) {
new ExternalClass2().externalMethod1();
}
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myMethod] = actualCodeStructure.classes[0].methods;
expect(myMethod.refs.sort()).toMatchObject([
{ name: 'ExternalClass1', isSystemType: false },
{ name: 'ExternalClass2', isSystemType: false }
]);
});
it('should extract parameters types passed to constructed as references', () => {
// Arrange
const code = `
public class MyClass {
public void myMethod() {
new ExternalClass1(new ExternalClass2(new ExternalClass3()));
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [myMethod] = actualCodeStructure.classes[0].methods;
expect(myMethod.refs.sort()).toMatchObject([
{ name: 'ExternalClass1', isSystemType: false },
{ name: 'ExternalClass2', isSystemType: false },
{ name: 'ExternalClass3', isSystemType: false }
]);
expect(myClass.refs.map(r => r.name).sort()).toEqual([
'ExternalClass1', 'ExternalClass2', 'ExternalClass3'
]);
});
it('should extract extended types as references', () => {
// Arrange
const code = `
public class MyClass extends ExternalClass1 {
public void myMethod() {
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
expect(myClass.refs.map(r => r.name).sort()).toEqual(['ExternalClass1']);
});
it('should extract refs from property getter/setter', () => {
// Arrange
const code = `
public class MyClass {
public String myProperty1 {
get {
return new ExternalClass1().externalMethod1();
}
set {
value.externalMethod2();
ExternalClass2.staticValue = value;
while(true) {
if (false) {
new ExternalClass3().test(value);
}
}
}
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [myProperty1] = actualCodeStructure.classes[0].properties;
expect(myProperty1.getter.refs.sort()).toMatchObject([
{ name: 'ExternalClass1' },
]);
expect(myProperty1.setter.refs.sort()).toMatchObject([
{ name: 'ExternalClass2' },
{ name: 'ExternalClass3' },
]);
expect(myClass.refs.map(r => r.name).sort()).toEqual([
'ExternalClass1', 'ExternalClass2', 'ExternalClass3', 'String'
]);
});
it('should parse ?? operator', () => {
// Arrange
const code = `
public class MyClass {
public Object fn() {
return new BaseClass() ?? new OtherClass();
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [fn] = myClass.methods;
expect(fn.refs.map(r => r.name).sort()).toEqual([
'BaseClass', 'OtherClass'
]);
});
it('should not parse catch parameters as references', () => {
// Arrange
const code = `
public class MyClass {
public void fn(String a) {
AuraHandledException ex = new AuraHandledException(new Class());
try {
} catch(AuraHandledException e) {
ex = e;
}
ex.Asserter.isNotNull(ex);
Assert.isNotNull(ex);
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [fn] = myClass.methods;
expect(fn.name).toEqual('fn');
expect(fn.parameters.length).toEqual(1);
expect(fn.refs.map(r => r.name).sort()).toEqual([
'Assert', 'AuraHandledException', 'AuraHandledException', 'Class', 'String'
]);
});
it('should not parse local variables as references', () => {
// Arrange
const code = `
public class MyClass {
Object local1 = null;
public void fn(String a) {
if (local1 != null) {
local2.someProperty.fn();
} else {
local2 = this.local1;
local1 = new ExternalClass();
}
}
Object local2 = null;
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myClass] = actualCodeStructure.classes;
const [fn] = myClass.methods;
expect(fn.name).toEqual('fn');
expect(fn.parameters.length).toEqual(1);
expect(fn.refs.map(r => r.name).sort()).toEqual([
'ExternalClass', 'String'
]);
});
it('should include namespaces in parsed references', () => {
// Arrange
const code = `
public class MyClass {
public void myMethod() {
System.Assert.isTrue(true, 'true');
OtherClass.Value = 1;
}
}
`;
// Act
const actualCodeStructure = new parser_1.Parser(code).getCodeStructure();
// Assert
const [myMethod] = actualCodeStructure.classes[0].methods;
expect([...new Set(myMethod.refs.map(r => r.name))].sort()).toEqual([
'OtherClass',
'System.Assert'
]);
});
});
});
//# sourceMappingURL=parser.test.js.map