Skip to content

Commit d4d3ae9

Browse files
author
Amit Joshi
committed
Merge branch 'main' of https://github.com/microsoft/powerplatform-vscode into users/amitjoshi/addSupportForCommonIntegrationTests
2 parents e4a04d0 + 485f05b commit d4d3ae9

File tree

1 file changed

+356
-0
lines changed

1 file changed

+356
-0
lines changed
Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
/*
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*/
5+
6+
import * as assert from 'assert';
7+
import * as vscode from 'vscode';
8+
import { ServerApiCompletionProvider, ServerApiDefinitions } from '../ServerApiCompletionProvider';
9+
10+
/**
11+
* Test suite for Server API autocomplete functionality
12+
*/
13+
suite('Server API Autocomplete Tests', () => {
14+
15+
suite('ServerApiDefinitions', () => {
16+
test('should return all required API namespace definitions', () => {
17+
const definitions = ServerApiDefinitions.getDefinitions();
18+
19+
// Ensure key namespaces exist
20+
assert.ok(definitions.find(d => d.name === 'Logger'));
21+
assert.ok(definitions.find(d => d.name === 'Connector'));
22+
assert.ok(definitions.find(d => d.name === 'Connector.HttpClient'));
23+
assert.ok(definitions.find(d => d.name === 'Connector.Dataverse'));
24+
assert.ok(definitions.find(d => d.name === 'Context'));
25+
assert.ok(definitions.find(d => d.name === 'Sitesetting'));
26+
assert.ok(definitions.find(d => d.name === 'Website'));
27+
assert.ok(definitions.find(d => d.name === 'Website.adx_defaultlanguage'));
28+
assert.ok(definitions.find(d => d.name === 'Website.adx_footerwebtemplateid'));
29+
assert.ok(definitions.find(d => d.name === 'Website.adx_headerwebtemplateid'));
30+
assert.ok(definitions.find(d => d.name === 'Website.adx_defaultbotconsumerid'));
31+
// New: User
32+
assert.ok(definitions.find(d => d.name === 'User'));
33+
assert.ok(definitions.find(d => d.name === 'User.owningbusinessunit'));
34+
assert.ok(definitions.find(d => d.name === 'User.owninguser'));
35+
assert.ok(definitions.find(d => d.name === 'User.ownerid'));
36+
assert.ok(definitions.find(d => d.name === 'User.modifiedby'));
37+
assert.ok(definitions.find(d => d.name === 'User.createdby'));
38+
});
39+
40+
test('should return specific namespace definition', () => {
41+
const loggerDef = ServerApiDefinitions.getNamespace('Logger');
42+
43+
assert.ok(loggerDef);
44+
assert.strictEqual(loggerDef.name, 'Logger');
45+
assert.ok(loggerDef.methods);
46+
assert.ok(loggerDef.methods.find(m => m.name === 'Log'));
47+
});
48+
49+
test('should return HttpClient namespace definition', () => {
50+
const httpClientDef = ServerApiDefinitions.getNamespace('Connector.HttpClient');
51+
52+
assert.ok(httpClientDef);
53+
assert.strictEqual(httpClientDef.name, 'Connector.HttpClient');
54+
assert.ok(httpClientDef.methods);
55+
assert.ok(httpClientDef.methods.find(m => m.name === 'Get'));
56+
assert.ok(httpClientDef.methods.find(m => m.name === 'Post'));
57+
assert.ok(httpClientDef.methods.find(m => m.name === 'Patch'));
58+
assert.ok(httpClientDef.methods.find(m => m.name === 'Put'));
59+
assert.ok(httpClientDef.methods.find(m => m.name === 'Delete'));
60+
});
61+
62+
test('should return Dataverse namespace definition', () => {
63+
const dataverseDef = ServerApiDefinitions.getNamespace('Connector.Dataverse');
64+
65+
assert.ok(dataverseDef);
66+
assert.strictEqual(dataverseDef.name, 'Connector.Dataverse');
67+
assert.ok(dataverseDef.methods);
68+
assert.ok(dataverseDef.methods.find(m => m.name === 'CreateRecord'));
69+
assert.ok(dataverseDef.methods.find(m => m.name === 'RetrieveRecord'));
70+
assert.ok(dataverseDef.methods.find(m => m.name === 'RetrieveMultipleRecords'));
71+
assert.ok(dataverseDef.methods.find(m => m.name === 'UpdateRecord'));
72+
assert.ok(dataverseDef.methods.find(m => m.name === 'DeleteRecord'));
73+
});
74+
75+
test('should return undefined for unknown namespace', () => {
76+
const unknownDef = ServerApiDefinitions.getNamespace('UnknownNamespace');
77+
assert.strictEqual(unknownDef, undefined);
78+
});
79+
});
80+
81+
suite('ServerApiCompletionProvider', () => {
82+
let provider: ServerApiCompletionProvider;
83+
84+
setup(() => {
85+
provider = new ServerApiCompletionProvider();
86+
});
87+
88+
test('should provide namespace completions after "Server."', async () => {
89+
const document = await vscode.workspace.openTextDocument({
90+
content: 'Server.',
91+
language: 'javascript'
92+
});
93+
94+
const position = new vscode.Position(0, 7); // After "Server."
95+
const completions = provider.provideCompletionItems(document, position);
96+
97+
assert.ok(Array.isArray(completions));
98+
const completionArray = completions as vscode.CompletionItem[];
99+
100+
// Should include top-level namespace completions
101+
assert.ok(completionArray.find(c => c.label === 'Logger'));
102+
assert.ok(completionArray.find(c => c.label === 'Connector'));
103+
assert.ok(completionArray.find(c => c.label === 'Context'));
104+
assert.ok(completionArray.find(c => c.label === 'Sitesetting'));
105+
assert.ok(completionArray.find(c => c.label === 'Website'));
106+
// New: User
107+
assert.ok(completionArray.find(c => c.label === 'User'));
108+
});
109+
110+
test('should provide sub-namespace completions for Connector', async () => {
111+
const document = await vscode.workspace.openTextDocument({
112+
content: 'Server.Connector.',
113+
language: 'javascript'
114+
});
115+
116+
const position = new vscode.Position(0, 17); // After "Server.Connector."
117+
const completions = provider.provideCompletionItems(document, position);
118+
119+
assert.ok(Array.isArray(completions));
120+
const completionArray = completions as vscode.CompletionItem[];
121+
122+
// Should include Connector sub-namespace completions
123+
assert.ok(completionArray.find(c => c.label === 'HttpClient'));
124+
assert.ok(completionArray.find(c => c.label === 'Dataverse'));
125+
});
126+
127+
test('should provide method completions for Logger namespace', async () => {
128+
const document = await vscode.workspace.openTextDocument({
129+
content: 'Server.Logger.',
130+
language: 'javascript'
131+
});
132+
133+
const position = new vscode.Position(0, 14); // After "Server.Logger."
134+
const completions = provider.provideCompletionItems(document, position);
135+
136+
assert.ok(Array.isArray(completions));
137+
const completionArray = completions as vscode.CompletionItem[];
138+
139+
// Should include Logger method completions
140+
assert.ok(completionArray.find(c => c.label === 'Log'));
141+
});
142+
143+
test('should provide method completions for HttpClient namespace', async () => {
144+
const document = await vscode.workspace.openTextDocument({
145+
content: 'Server.Connector.HttpClient.',
146+
language: 'javascript'
147+
});
148+
149+
const position = new vscode.Position(0, 28); // After "Server.Connector.HttpClient."
150+
const completions = provider.provideCompletionItems(document, position);
151+
152+
assert.ok(Array.isArray(completions));
153+
const completionArray = completions as vscode.CompletionItem[];
154+
155+
// Should include HttpClient method completions
156+
assert.ok(completionArray.find(c => c.label === 'Get'));
157+
assert.ok(completionArray.find(c => c.label === 'Post'));
158+
assert.ok(completionArray.find(c => c.label === 'Patch'));
159+
assert.ok(completionArray.find(c => c.label === 'Put'));
160+
assert.ok(completionArray.find(c => c.label === 'Delete'));
161+
});
162+
163+
test('should provide method completions for Dataverse namespace', async () => {
164+
const document = await vscode.workspace.openTextDocument({
165+
content: 'Server.Connector.Dataverse.',
166+
language: 'javascript'
167+
});
168+
169+
const position = new vscode.Position(0, 27); // After "Server.Connector.Dataverse."
170+
const completions = provider.provideCompletionItems(document, position);
171+
172+
assert.ok(Array.isArray(completions));
173+
const completionArray = completions as vscode.CompletionItem[];
174+
175+
// Should include Dataverse method completions
176+
assert.ok(completionArray.find(c => c.label === 'CreateRecord'));
177+
assert.ok(completionArray.find(c => c.label === 'RetrieveRecord'));
178+
assert.ok(completionArray.find(c => c.label === 'RetrieveMultipleRecords'));
179+
assert.ok(completionArray.find(c => c.label === 'UpdateRecord'));
180+
assert.ok(completionArray.find(c => c.label === 'DeleteRecord'));
181+
});
182+
183+
test('should provide property completions for Context namespace', async () => {
184+
const document = await vscode.workspace.openTextDocument({
185+
content: 'Server.Context.',
186+
language: 'javascript'
187+
});
188+
189+
const position = new vscode.Position(0, 15);
190+
const completions = provider.provideCompletionItems(document, position);
191+
192+
assert.ok(Array.isArray(completions));
193+
const completionArray = completions as vscode.CompletionItem[];
194+
195+
const expectedProps = ['ActivityId','Body','FunctionName','Headers','HttpMethod','QueryParameters','ServerLogicName','Url'];
196+
expectedProps.forEach(p => assert.ok(completionArray.find(c => c.label === p), `Missing Context property: ${p}`));
197+
});
198+
199+
test('should provide method completion for Sitesetting namespace', async () => {
200+
const document = await vscode.workspace.openTextDocument({
201+
content: 'Server.Sitesetting.',
202+
language: 'javascript'
203+
});
204+
205+
const position = new vscode.Position(0, 19);
206+
const completions = provider.provideCompletionItems(document, position);
207+
208+
assert.ok(Array.isArray(completions));
209+
const completionArray = completions as vscode.CompletionItem[];
210+
211+
assert.ok(completionArray.find(c => c.label === 'Get'));
212+
});
213+
214+
test('should provide property completions for Website namespace', async () => {
215+
const document = await vscode.workspace.openTextDocument({
216+
content: 'Server.Website.',
217+
language: 'javascript'
218+
});
219+
220+
const position = new vscode.Position(0, 15);
221+
const completions = provider.provideCompletionItems(document, position);
222+
223+
assert.ok(Array.isArray(completions));
224+
const completionArray = completions as vscode.CompletionItem[];
225+
226+
const expectedTopProps = ['statecode','statuscode','adx_websiteid','adx_primarydomainname','adx_name','adx_defaultlanguage','adx_footerwebtemplateid','adx_headerwebtemplateid','adx_defaultbotconsumerid','isCoreEntity'];
227+
expectedTopProps.forEach(p => assert.ok(completionArray.find(c => c.label === p), `Missing Website property: ${p}`));
228+
});
229+
230+
test('should provide nested property completions for Website entity references', async () => {
231+
const document = await vscode.workspace.openTextDocument({
232+
content: 'Server.Website.adx_defaultlanguage.',
233+
language: 'javascript'
234+
});
235+
236+
const position = new vscode.Position(0, 33);
237+
const completions = provider.provideCompletionItems(document, position);
238+
239+
assert.ok(Array.isArray(completions));
240+
const completionArray = completions as vscode.CompletionItem[];
241+
242+
['LogicalName','Id','Name'].forEach(p => assert.ok(completionArray.find(c => c.label === p), `Missing nested property: ${p}`));
243+
});
244+
245+
test('should provide property completions for User namespace', async () => {
246+
const document = await vscode.workspace.openTextDocument({
247+
content: 'Server.User.',
248+
language: 'javascript'
249+
});
250+
251+
const position = new vscode.Position(0, 12);
252+
const completions = provider.provideCompletionItems(document, position);
253+
254+
assert.ok(Array.isArray(completions));
255+
const completionArray = completions as vscode.CompletionItem[];
256+
257+
const expectedProps = [
258+
'customertypecode','address2_addresstypecode','merged','adx_identity_securitystamp','territorycode','emailaddress1','haschildrencode','adx_identity_passwordhash','preferredappointmenttimecode','adx_profilemodifiedon','isbackofficecustomer','owningbusinessunit','owninguser','adx_profilealert','lastname','donotpostalmail','marketingonly','donotphone','preferredcontactmethodcode','adx_identity_locallogindisabled','educationcode','ownerid','adx_identity_logonenabled','customersizecode','firstname','yomifullname','adx_identity_lockoutenabled','adx_profileisanonymous','donotemail','address2_shippingmethodcode','statuscode','createdon','donotsendmm','donotfax','leadsourcecode','adx_identity_accessfailedcount','adx_confirmremovepassword','modifiedon','creditonhold','adx_identity_emailaddress1confirmed','msdyn_isminor','adx_identity_username','msdyn_isminorwithparentalconsent','address3_addressid','donotbulkemail','adx_identity_twofactorenabled','modifiedby','followemail','shippingmethodcode','createdby','donotbulkpostalmail','contactid','msdyn_disablewebtracking','adx_identity_mobilephoneconfirmed','participatesinworkflow','statecode','address2_addressid'
259+
];
260+
expectedProps.forEach(p => assert.ok(completionArray.find(c => c.label === p), `Missing User property: ${p}`));
261+
});
262+
263+
test('should provide nested property completions for User entity references', async () => {
264+
const document = await vscode.workspace.openTextDocument({
265+
content: 'Server.User.ownerid.',
266+
language: 'javascript'
267+
});
268+
269+
const position = new vscode.Position(0, 22);
270+
const completions = provider.provideCompletionItems(document, position);
271+
272+
assert.ok(Array.isArray(completions));
273+
const completionArray = completions as vscode.CompletionItem[];
274+
275+
['LogicalName','Id','Name'].forEach(p => assert.ok(completionArray.find(c => c.label === p), `Missing nested property: ${p}`));
276+
});
277+
278+
test('should not provide completions for non-Server context', async () => {
279+
const document = await vscode.workspace.openTextDocument({
280+
content: 'console.',
281+
language: 'javascript'
282+
});
283+
284+
const position = new vscode.Position(0, 8); // After "console."
285+
const completions = provider.provideCompletionItems(document, position);
286+
287+
assert.ok(Array.isArray(completions));
288+
const completionArray = completions as vscode.CompletionItem[];
289+
290+
// Should not provide any completions for non-Server context
291+
assert.strictEqual(completionArray.length, 0);
292+
});
293+
294+
test('should provide completions with proper documentation', async () => {
295+
const document = await vscode.workspace.openTextDocument({
296+
content: 'Server.Logger.',
297+
language: 'javascript'
298+
});
299+
300+
const position = new vscode.Position(0, 14); // After "Server.Logger."
301+
const completions = provider.provideCompletionItems(document, position);
302+
303+
assert.ok(Array.isArray(completions));
304+
const completionArray = completions as vscode.CompletionItem[];
305+
306+
const logCompletion = completionArray.find(c => c.label === 'Log');
307+
assert.ok(logCompletion);
308+
assert.ok(logCompletion.documentation);
309+
assert.strictEqual(logCompletion.kind, vscode.CompletionItemKind.Method);
310+
});
311+
});
312+
});
313+
314+
/**
315+
* Sample usage examples for testing Server API autocomplete in JavaScript files
316+
*/
317+
export const ServerApiUsageExamples = {
318+
logger: `
319+
// Logger examples - type "Server.Logger." to see autocomplete in .js files
320+
Server.Logger.Log('Application started');
321+
Server.Logger.Log('User logged in: ' + userId);
322+
Server.Logger.Log('Processing request for: ' + requestData);
323+
`,
324+
325+
httpClient: `
326+
// HTTP Client examples - type "Server.Connector.HttpClient." to see autocomplete in .js files
327+
// Note: This will block access to Dataverse
328+
const headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token };
329+
const response = Server.Connector.HttpClient.Get('https://api.example.com/data', headers);
330+
const postResult = Server.Connector.HttpClient.Post('https://api.example.com/users', JSON.stringify(userData), headers);
331+
const patchResult = Server.Connector.HttpClient.Patch('https://api.example.com/users/123', JSON.stringify(updatedData), headers);
332+
const putResult = Server.Connector.HttpClient.Put('https://api.example.com/users/123', JSON.stringify(updatedData), headers);
333+
Server.Connector.HttpClient.Delete('https://api.example.com/users/123', headers);
334+
`,
335+
336+
dataverse: `
337+
// Dataverse examples - type "Server.Connector.Dataverse." to see autocomplete in .js files
338+
const contactPayload = JSON.stringify({ firstname: 'John', lastname: 'Doe', emailaddress1: 'john@example.com' });
339+
const newContactId = Server.Connector.Dataverse.CreateRecord('contacts', contactPayload);
340+
341+
const contact = Server.Connector.Dataverse.RetrieveRecord('contacts', contactId, '$select=firstname,lastname,emailaddress1');
342+
const contacts = Server.Connector.Dataverse.RetrieveMultipleRecords('contacts', '$filter=lastname eq \\'Smith\\'&$select=firstname,lastname');
343+
344+
const updatePayload = JSON.stringify({ firstname: 'Jane' });
345+
Server.Connector.Dataverse.UpdateRecord('contacts', contactId, updatePayload);
346+
347+
Server.Connector.Dataverse.DeleteRecord('contacts', contactId);
348+
`,
349+
350+
connector: `
351+
// Connector examples - type "Server.Connector." to see sub-namespace options in .js files
352+
// Choose between HttpClient or Dataverse:
353+
Server.Connector.HttpClient.Get(url, headers); // For external API calls
354+
Server.Connector.Dataverse.RetrieveRecord(entitySetName, id, options); // For Dataverse operations
355+
`
356+
};

0 commit comments

Comments
 (0)