-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: handle namespaces when running tests #126
Changes from all commits
e027dc0
cc260d4
a167cf7
3823bbe
110b8bf
29a59a2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,10 @@ import { | |
PerClassCoverage, | ||
OutputDirConfig, | ||
ApexTestResultRecord, | ||
SyncTestFailure | ||
SyncTestFailure, | ||
TestItem, | ||
TestLevel, | ||
NamespaceQueryResult | ||
} from './types'; | ||
import * as util from 'util'; | ||
import { nls } from '../i18n'; | ||
|
@@ -38,14 +41,144 @@ import { ApexDiagnostic } from '../utils/types'; | |
// Tooling API query char limit is 100,000 after v48; REST API limit for uri + headers is 16,348 bytes | ||
// local testing shows query char limit to be closer to ~12,400 | ||
const QUERY_CHAR_LIMIT = 12400; | ||
|
||
const CLASS_ID_PREFIX = '01p'; | ||
export class TestService { | ||
public readonly connection: Connection; | ||
|
||
constructor(connection: Connection) { | ||
this.connection = connection; | ||
} | ||
|
||
// utils to build test run payloads that may contain namespaces | ||
public async buildSyncPayload( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved this functionality over to the library- we'll need these utils for the plugin and in VS Code. Consumers can still build their own payloads when calling |
||
testLevel: TestLevel, | ||
tests?: string, | ||
classnames?: string | ||
): Promise<SyncTestConfiguration> { | ||
let payload: SyncTestConfiguration; | ||
if (tests) { | ||
payload = await this.buildTestPayload(tests); | ||
const classes = payload.tests?.map(testItem => { | ||
if (testItem.className) { | ||
return testItem.className; | ||
} | ||
}); | ||
if (new Set(classes).size !== 1) { | ||
return Promise.reject(new Error(nls.localize('syncClassErr'))); | ||
} | ||
} else { | ||
const prop = classnames.toLowerCase().startsWith(CLASS_ID_PREFIX) | ||
? 'classId' | ||
: 'className'; | ||
payload = { | ||
tests: [{ [prop]: classnames }], | ||
testLevel | ||
}; | ||
} | ||
return payload; | ||
} | ||
|
||
public async buildAsyncPayload( | ||
testLevel: TestLevel, | ||
tests?: string, | ||
classNames?: string, | ||
suiteNames?: string | ||
): Promise<AsyncTestConfiguration | AsyncTestArrayConfiguration> { | ||
if (tests) { | ||
return (await this.buildTestPayload( | ||
tests | ||
)) as AsyncTestArrayConfiguration; | ||
} else if (classNames) { | ||
return await this.buildAsyncClassPayload(classNames); | ||
} else { | ||
return { | ||
suiteNames, | ||
testLevel | ||
}; | ||
} | ||
} | ||
|
||
private async buildTestPayload( | ||
testNames: string | ||
): Promise<AsyncTestArrayConfiguration | SyncTestConfiguration> { | ||
const testNameArray = testNames.split(','); | ||
const testItems: TestItem[] = []; | ||
let namespaces: Set<string>; | ||
|
||
for (const test of testNameArray) { | ||
if (test.indexOf('.') > 0) { | ||
const testParts = test.split('.'); | ||
if (testParts.length === 3) { | ||
testItems.push({ | ||
namespace: `${testParts[0]}`, | ||
className: `${testParts[1]}`, | ||
testMethods: [testParts[2]] | ||
}); | ||
} else { | ||
if (typeof namespaces === 'undefined') { | ||
namespaces = await this.queryNamespaces(); | ||
} | ||
|
||
if (namespaces.has(testParts[0])) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this will error out if we run a namespaced tests against an org that returns no namespaces. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind it does work correctly. Can we add a test for that scenario ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done - tests that the argument we build will treat the namespace as a classname & the class as a test method so eventually the test run would error out. |
||
testItems.push({ | ||
namespace: `${testParts[0]}`, | ||
className: `${testParts[1]}` | ||
}); | ||
} else { | ||
testItems.push({ | ||
className: testParts[0], | ||
testMethods: [testParts[1]] | ||
}); | ||
} | ||
} | ||
} else { | ||
testItems.push({ className: test }); | ||
} | ||
} | ||
|
||
return { | ||
tests: testItems, | ||
testLevel: TestLevel.RunSpecifiedTests | ||
}; | ||
} | ||
|
||
private async buildAsyncClassPayload( | ||
classNames: string | ||
): Promise<AsyncTestArrayConfiguration> { | ||
const classNameArray = classNames.split(',') as string[]; | ||
const classItems = classNameArray.map(item => { | ||
const classParts = item.split('.'); | ||
if (classParts.length > 1) { | ||
return { | ||
namespace: `${classParts[0]}`, | ||
className: `${classParts[1]}` | ||
}; | ||
} | ||
return { className: item } as TestItem; | ||
}); | ||
return { tests: classItems, testLevel: TestLevel.RunSpecifiedTests }; | ||
} | ||
|
||
public async queryNamespaces(): Promise<Set<string>> { | ||
const installedNsQuery = 'SELECT NamespacePrefix FROM PackageLicense'; | ||
const installedNsResult = (await this.connection.query( | ||
installedNsQuery | ||
)) as NamespaceQueryResult; | ||
const installedNamespaces = installedNsResult.records.map(record => { | ||
return record.NamespacePrefix; | ||
}); | ||
|
||
const orgNsQuery = 'SELECT NamespacePrefix FROM Organization'; | ||
const orgNsResult = (await this.connection.query( | ||
orgNsQuery | ||
)) as NamespaceQueryResult; | ||
const orgNamespaces = orgNsResult.records.map(record => { | ||
return record.NamespacePrefix; | ||
}); | ||
|
||
return new Set([...orgNamespaces, ...installedNamespaces]); | ||
} | ||
|
||
// Synchronous Test Runs | ||
public async runTestSynchronous( | ||
options: SyncTestConfiguration, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When are keys underscore vs camelcase?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmmm good question, looking through right now and looks like we've got a bit of everything : - ) I'll update the ones in the library package for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I'll open a follow up for everything, it's going to get messy in this PR