diff --git a/docs/graph/cloud-communications.md b/docs/graph/cloud-communications.md index 468d0b58f..4ba32dfd6 100644 --- a/docs/graph/cloud-communications.md +++ b/docs/graph/cloud-communications.md @@ -47,6 +47,7 @@ import "@pnp/graph/cloud-communications"; const graph = graphfi(...); +// Session ID is the Application's client id const presenceMe = await graph.me.presence.setPresence( availability: "Busy", activity:"InACall", diff --git a/package-lock.json b/package-lock.json index cdba8e238..2a594726b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -748,9 +748,9 @@ } }, "node_modules/@pnp/core": { - "version": "4.0.0-alpha0-v4nightly.20240412", - "resolved": "https://registry.npmjs.org/@pnp/core/-/core-4.0.0-alpha0-v4nightly.20240412.tgz", - "integrity": "sha512-CETGc6aYHRPSQtKRXLUKv5COLVy28QhKfjeqTEXmaleweRfRghvf3X0AYijYROQJL6//m5pHusA84skSwEI1iw==", + "version": "4.0.0-alpha0-v4nightly.20240418", + "resolved": "https://registry.npmjs.org/@pnp/core/-/core-4.0.0-alpha0-v4nightly.20240418.tgz", + "integrity": "sha512-BiEJD/k+dRy9HJ6qgUMSq5ngBCNYLBQhhVNzT8i4oaAyw3gs7UR4u46Z1q/fW5o8d/tSsH2KM0NtbPYWh2KrSQ==", "dev": true, "dependencies": { "tslib": "2.4.1" @@ -997,9 +997,9 @@ "dev": true }, "node_modules/@types/qs": { - "version": "6.9.14", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz", - "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==", + "version": "6.9.15", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.15.tgz", + "integrity": "sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==", "dev": true }, "node_modules/@types/range-parser": { @@ -2028,9 +2028,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001610", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001610.tgz", - "integrity": "sha512-QFutAY4NgaelojVMjY63o6XlZyORPaLfyMnsl3HgnWdJUcX6K0oaJymHjH8PT5Gk7sTm8rvC/c5COUQKXqmOMA==", + "version": "1.0.30001611", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001611.tgz", + "integrity": "sha512-19NuN1/3PjA3QI8Eki55N8my4LzfkMCRLgCVfrl/slbSAchQfV0+GwjPrK3rq37As4UCLlM/DHajbKkAqbv92Q==", "dev": true, "funding": [ { @@ -2708,9 +2708,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.736", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.736.tgz", - "integrity": "sha512-Rer6wc3ynLelKNM4lOCg7/zPQj8tPOCB2hzD32PX9wd3hgRRi9MxEbmkFCokzcEhRVMiOVLjnL9ig9cefJ+6+Q==", + "version": "1.4.741", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.741.tgz", + "integrity": "sha512-AyTBZqDoS7/mvQK22gOQpjxbeV8iPeUBTvYlEh/1S9dKAHgQdxuF49g9rLbj0cRKtqH8PzLJzqT3nAdl+qoZTA==", "dev": true }, "node_modules/emoji-regex": { diff --git a/packages/graph/mail/users.ts b/packages/graph/mail/users.ts index af84b9d85..482876f09 100644 --- a/packages/graph/mail/users.ts +++ b/packages/graph/mail/users.ts @@ -14,7 +14,7 @@ declare module "../users/types" { readonly mailFolders: IMailFolders; readonly outlook: IOutlook; readonly focusedInboxOverrides: IFocusedInboxOverrides; - sendMail(message: IMessageType): Promise; + sendMail(message: IMessageType, saveToSentItems?: boolean): Promise; translateExchangeIds(translateExchangeIds: ITranslateExchangeIds): Promise; } interface IUser { @@ -23,7 +23,7 @@ declare module "../users/types" { readonly mailFolders: IMailFolders; readonly outlook: IOutlook; readonly focusedInboxOverrides: IFocusedInboxOverrides; - sendMail(message: IMessageType): Promise; + sendMail(message: IMessageType, saveToSentItems?: boolean): Promise; translateExchangeIds(translateExchangeIds: ITranslateExchangeIds): Promise; } } @@ -34,8 +34,8 @@ addProp(_User, "mailFolders", MailFolders); addProp(_User, "outlook", Outlook); addProp(_User, "focusedInboxOverrides", FocusedInboxOverrides, "inferenceClassification/overrides"); -_User.prototype.sendMail = function (this: _User, message: IMessageType): Promise { - return graphPost(User(this, "sendMail"), body(message)); +_User.prototype.sendMail = function (this: _User, message: IMessageType, saveToSentItems = true): Promise { + return graphPost(User(this, "sendMail"), body({message, saveToSentItems})); }; /** diff --git a/test/graph/batch.ts b/test/graph/batch.ts index 69016324e..1a137cb61 100644 --- a/test/graph/batch.ts +++ b/test/graph/batch.ts @@ -75,6 +75,7 @@ describe("Batching", function () { return expect(order.toString()).to.eql(expected.toString()); })); + // This logs to the console when it passes, ignore those messages it("Should work with the same Queryable when properly cloned (Advanced)", pnpTest("76fbb5bf-dfc5-4230-a9df-ef1ecc2ee7a4", async function () { const users = this.pnp.graph.users; @@ -87,9 +88,16 @@ describe("Batching", function () { this.pnp.graph.users.using(batchedBehavior)(); this.pnp.graph.users.using(batchedBehavior)(); - return expect(execute()).to.eventually.be.fulfilled; + let success = true; + try { + await execute(); + } catch (err) { + success = false; + } + return expect(success).to.be.true; })); + // This logs to the console when it passes, ignore those messages it("Should work with the same Queryable when properly cloned by factory (Advanced)", pnpTest("d0ba8747-a776-4f4e-be09-6a6126dc1e06", async function () { const users = this.pnp.graph.users; @@ -101,9 +109,16 @@ describe("Batching", function () { Users(users).using(batchedBehavior)(); Users(users).using(batchedBehavior)(); - return expect(execute()).to.eventually.be.fulfilled; + let success = true; + try { + await execute(); + } catch (err) { + success = false; + } + return expect(success).to.be.true; })); + // This logs to the console when it passes, ignore those messages it("Should fail with the same Queryable (Advanced)", pnpTest("ca3ae3bb-1729-47d9-abea-e531cd7817dc", async function () { const users = this.pnp.graph.users; @@ -113,15 +128,24 @@ describe("Batching", function () { users(); - const p = users(); - - const p2 = execute(); + let pSuccess = false; + try { + await users(); + pSuccess = true; + } catch (err) { + // do nothing + } - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(p).to.eventually.be.rejected; + let p2Success = true; + try { + await execute(); + } catch (err) { + // do nothing + p2Success = false; + } + const success = (!pSuccess && p2Success); - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - expect(p2).to.eventually.be.fulfilled; + return expect(success).to.be.true; })); }); diff --git a/test/graph/cloud-communications.ts b/test/graph/cloud-communications.ts index b1c9ad0e4..e591821bb 100644 --- a/test/graph/cloud-communications.ts +++ b/test/graph/cloud-communications.ts @@ -3,7 +3,6 @@ import "@pnp/graph/users"; import "@pnp/graph/cloud-communications"; import { stringIsNullOrEmpty } from "@pnp/core"; - describe("Cloud-Communications", function () { let testUserId = ""; let sessionId = ""; @@ -13,59 +12,87 @@ describe("Cloud-Communications", function () { if (!this.pnp.settings.enableWebTests || stringIsNullOrEmpty(this.pnp.settings.testUser)) { this.skip(); } - testUserId = (await this.pnp.graph.users.getById(this.pnp.settings.testUser.substring(this.pnp.settings.testUser.lastIndexOf("|") + 1))()).id; - sessionId = this.pnp.settings.graph.id; + testUserId = (await this.pnp.graph.users.getById(this.pnp.settings.testUser.substring(this.pnp.settings.testUser.lastIndexOf("|") + 1))()).id; + sessionId = this.pnp.settings.graph.msal.init.auth.clientId; }); - it.skip("Get User Presence", async function () { + it("Get User Presence", async function () { const presence = await this.pnp.graph.users.getById(testUserId).presence(); return expect(presence).is.not.null; }); - it.skip("Get Presence for Multiple Users", async function () { - const presence = await this.pnp.graph.communications.getPresencesByUserId([testUserId,testUserId]); - return expect(presence.length).is.equals(2); + it("Get Presence for Multiple Users", async function () { + const presence = await this.pnp.graph.communications.getPresencesByUserId([testUserId]); + return expect(presence.length).is.equals(1); }); it("Set User Presence", async function () { - return expect(this.pnp.graph.users.getById(testUserId).presence.setPresence({ - availability: "Busy", - activity:"InACall", - sessionId: sessionId, - expirationDuration: "PT5M", - })).eventually.be.fulfilled; + let success = true; + try { + await this.pnp.graph.users.getById(testUserId).presence.setPresence({ + availability: "Busy", + activity: "InACall", + sessionId: sessionId, + expirationDuration: "PT5M", + }); + } catch (err) { + success = false; + } + return expect(success).to.be.true; }); it("Clear User Presence", async function () { - return expect(this.pnp.graph.users.getById(testUserId).presence.clearPresence(sessionId)).eventually.be.fulfilled; + let success = true; + try { + await this.pnp.graph.users.getById(testUserId).presence.clearPresence(sessionId); + } catch (err) { + success = false; + } + return expect(success).to.be.true; }); it("Set User Preferred Presence", async function () { - return expect(this.pnp.graph.users.getById(testUserId).presence.setPreferredPresence({ - availability: "Available", - activity:"Available", - expirationDuration: "PT5M", - })).eventually.be.fulfilled; + let success = true; + try { + await this.pnp.graph.users.getById(testUserId).presence.setPreferredPresence({ + availability: "Available", + activity: "Available", + expirationDuration: "PT5M", + }); + } catch (err) { + success = false; + } + return expect(success).to.be.true; }); it("Clear User Preferred Presence", async function () { - return expect(this.pnp.graph.users.getById(testUserId).presence.clearPreferredPresence()).eventually.be.fulfilled; + let success = true; + try { + await this.pnp.graph.users.getById(testUserId).presence.clearPreferredPresence(); + } catch (err) { + success = false; + } + return expect(success).to.be.true; }); it("Set User Status Message", async function () { - const date: Date = new Date(); - date.setDate(date.getDate() + 1); - - return expect(this.pnp.graph.users.getById(testUserId).presence.setStatusMessage({ - message:{ - content: "Test Sample Message", - contentType: "text", - }, - expiryDateTime:{ - dateTime: date.toISOString(), - timeZone: "Pacific Standard Time", - }, - })).eventually.be.fulfilled; + let success = true; + try { + const date: Date = new Date(); + date.setDate(date.getDate() + 1); + await this.pnp.graph.users.getById(testUserId).presence.setStatusMessage({ + message: { + content: "Test Sample Message", + contentType: "text", + }, + expiryDateTime: { + dateTime: date.toISOString(), + timeZone: "Pacific Standard Time", + }, + }); + } catch (err) { + success = false; + } + return expect(success).to.be.true; }); - }); diff --git a/test/graph/contacts.ts b/test/graph/contacts.ts index 734fe2971..c62d8f0b7 100644 --- a/test/graph/contacts.ts +++ b/test/graph/contacts.ts @@ -90,10 +90,15 @@ describe("Contacts", function () { await this.pnp.graph.users.getById(testUserName).contacts.getById(contact.id).update({ birthday: "1986-05-30" }); const contact2 = await this.pnp.graph.users.getById(testUserName).contacts.getById(contact.id)(); // Clean up the added contact - await this.pnp.graph.users.getById(testUserName).contacts.getById(contact.id).delete(); + try { + await this.pnp.graph.users.getById(testUserName).contacts.getById(contact.id).delete(); + } catch (err) { + console.log(err.message); + } return expect(contact2.birthday).equals("1986-05-30T11:59:00Z"); }); + // This logs to the console when it passes, ignore those messages it("Delete Contact", async function () { // Add a contact that we can then delete const testContactName = `TestUser_${getRandomString(4)}`; @@ -105,15 +110,15 @@ describe("Contacts", function () { let deletedUserFound = false; try { - - // If we try to find a user that doesn"t exist this returns a 404 + // This passes the first time through, expecting it to fail on second pass. + // If we try to find a user that doesn't exist this returns a 404 await this.pnp.graph.users.getById(testUserName).contacts.getById(contact.id)(); deletedUserFound = true; } catch (e) { if (e?.isHttpRequestError) { if ((e).status === 404) { - console.error((e).statusText); + // do nothing } } else { console.log(e.message); @@ -154,7 +159,7 @@ describe("Contacts", function () { }); it("Update Contact Folder", async function () { - const folderDisplayName = "Folder_Updated" + getRandomString(4); + const folderDisplayName = `Folder_Updated_${getRandomString(4)}`; let folderId = null; let folderAfterUpdate = null; try { @@ -168,12 +173,17 @@ describe("Contacts", function () { } finally { // Clean up the added contact if (folderId != null) { - await this.pnp.graph.users.getById(testUserName).contactFolders.getById(folderId).delete(); + try { + await this.pnp.graph.users.getById(testUserName).contactFolders.getById(folderId).delete(); + } catch (err) { + console.log(err.message); + } } } return expect(folderAfterUpdate?.displayName).equals(folderDisplayName); }); + // This logs to the console when it passes, ignore those messages it("Delete Contact Folder", async function () { // Add a folder that we can then delete const testFolderName = `TestFolder_${getRandomString(4)}`; @@ -182,15 +192,15 @@ describe("Contacts", function () { let deletedFolderFound = false; try { - - // If we try to find a folder that doesn"t exist this returns a 404 + // This passes the first time through, expecting it to fail on second pass. + // If we try to find a folder that doesn't exist this returns a 404 await this.pnp.graph.users.getById(testUserName).contactFolders.getById(folder.id)(); deletedFolderFound = true; } catch (e) { if (e?.isHttpRequestError) { if ((e).status === 404) { - console.error((e).statusText); + // do nothing } } else { console.log(e.message); diff --git a/test/graph/files.ts b/test/graph/files.ts index 9e3a40c79..c2bf8e77d 100644 --- a/test/graph/files.ts +++ b/test/graph/files.ts @@ -64,8 +64,6 @@ describe("Drive", function () { return expect(list).is.not.null; }); - - // TODO: Failing with Timeout it("Get Recent Drive Items", async function () { if (stringIsNullOrEmpty(driveId)) { this.skip(); @@ -233,6 +231,7 @@ describe("Drive", function () { return expect(thumbnails).is.not.null; }); + // This logs to the console when it passes, ignore those messages it("Delete Drive Item", async function () { if (stringIsNullOrEmpty(driveId)) { this.skip(); @@ -254,6 +253,7 @@ describe("Drive", function () { return expect(driveItemId).to.be.null; }); + // This logs to the console when it passes, ignore those messages it("Permanently Delete Drive Item", async function () { if (stringIsNullOrEmpty(driveId)) { this.skip(); @@ -349,7 +349,6 @@ describe("Drive", function () { return expect(driveItemUpdate.name).to.eq(testFileName2); }); - // TODO: Failing timeout it("Convert Drive Item", async function () { if (stringIsNullOrEmpty(driveId)) { this.skip(); diff --git a/test/graph/group-conversations.ts b/test/graph/group-conversations.ts index 650ee9369..28dc4edc4 100644 --- a/test/graph/group-conversations.ts +++ b/test/graph/group-conversations.ts @@ -11,7 +11,6 @@ import { import { getRandomString } from "@pnp/core"; describe("Group Conversations", function () { - let testUserName = ""; let groupId = ""; const draftPost: IPostType = { @@ -38,21 +37,15 @@ describe("Group Conversations", function () { before(async function () { - if (!this.pnp.settings.enableWebTests) { + if (!this.pnp.settings.enableWebTests || !this.pnp.settings.testGroupId) { this.skip(); } const userInfo = await getValidUser.call(this); - testUserName = userInfo.userPrincipalName; draftPost.from.emailAddress.address = userInfo.userPrincipalName; draftPost.from.emailAddress.name = userInfo.displayName; postForwardInfo.toRecipients[0].emailAddress.address = userInfo.userPrincipalName; postForwardInfo.toRecipients[0].emailAddress.name = userInfo.displayName; - const groups = await this.pnp.graph.users.getById(testUserName).joinedTeams(); - if (groups.length > 0) { - groupId = groups[0].id; - } else { - this.skip(); - } + groupId = this.pnp.settings.testGroupId; }); describe("Group Conversations", function () { @@ -99,7 +92,8 @@ describe("Group Conversations", function () { return expect(post).to.have.property("id"); }); - it("post reply", async function () { + // Even though docs say you can do this with app permissions throwing a 403, that said conversations do not support app permissions so it feels like a bug in the docs. + it.skip("post reply", async function () { const conversations = await this.pnp.graph.groups.getById(groupId).conversations(); const convThreads = await this.pnp.graph.groups.getById(groupId).conversations.getById(conversations[0].id).threads(); const threadPost = await this.pnp.graph.groups.getById(groupId).conversations.getById(conversations[0].id).threads.getById(convThreads[0].id).posts(); @@ -111,7 +105,8 @@ describe("Group Conversations", function () { return expect(reply).to.have.property("id"); }); - it("post forward", async function () { + // Even though docs say you can do this with app permissions throwing a 403, that said conversations do not support app permissions so it feels like a bug in the docs. + it.skip("post forward", async function () { let success = false; const conversations = await this.pnp.graph.groups.getById(groupId).conversations(); const convThreads = await this.pnp.graph.groups.getById(groupId).conversations.getById(conversations[0].id).threads(); diff --git a/test/graph/mail-folders.ts b/test/graph/mail-folders.ts index 22db3b6e0..7b09a1f03 100644 --- a/test/graph/mail-folders.ts +++ b/test/graph/mail-folders.ts @@ -113,6 +113,7 @@ describe("Mail: Folders", function () { return expect(success).to.be.true; }); + // This logs to the console when it passes, ignore those messages it("Mail: Folder Delete", async function () { const f: IMailFolder = JSON.parse(JSON.stringify(draftFolder)); f.displayName = `${testFolderName} ${getRandomString(8)}`; diff --git a/test/graph/mail-mailbox.ts b/test/graph/mail-mailbox.ts index e8229d37e..b26cba65a 100644 --- a/test/graph/mail-mailbox.ts +++ b/test/graph/mail-mailbox.ts @@ -134,6 +134,7 @@ describe("Mail: Mailbox", function () { return expect(success).to.be.true; }); + // This logs to the console when it passes, ignore those messages it("Mailbox: Delete Focused Inbox Override", async function () { const f: InferenceClassificationOverride = JSON.parse(JSON.stringify(override)); f.senderEmailAddress.name = `${testName} ${getRandomString(8)}`; diff --git a/test/graph/mail-messages.ts b/test/graph/mail-messages.ts index aa30fecdf..50b69b161 100644 --- a/test/graph/mail-messages.ts +++ b/test/graph/mail-messages.ts @@ -97,6 +97,7 @@ describe("Mail: Messages", function () { return expect(success).to.be.true; }); + // This logs to the console when it passes, ignore those messages it("Mail: Delete Message", async function () { const m = JSON.parse(JSON.stringify(draftMessage)); const draft = await user.messages.add(m); @@ -162,7 +163,7 @@ describe("Mail: Messages", function () { m.subject = `PnPjs Test Message ${getRandomString(8)}`; let success = false; try{ - await user.sendMail(m); + await user.sendMail(m, false); success = true; }catch(err){ // do nothing @@ -170,7 +171,8 @@ describe("Mail: Messages", function () { return success; }); - it("Mail: Create Draft Reply Message", async function () { + // Cannot guarantee that there is email message in the inbox suitable to reply to + it.skip("Mail: Create Draft Reply Message", async function () { const inboxMessage = await user.mailFolders.getById(inboxFolder).messages.top(1)(); if (inboxMessage.length === 1) { let success = false; @@ -189,7 +191,8 @@ describe("Mail: Messages", function () { // Skipping because it would possibly send an email to someone who didn't expect it }); - it("Mail: Create Draft Reply-All Message", async function () { + // Cannot guarantee that there is email message in the inbox suitable to reply to + it.skip("Mail: Create Draft Reply-All Message", async function () { const inboxMessage = await user.mailFolders.getById(inboxFolder).messages.top(1)(); if (inboxMessage.length === 1) { let success = false; diff --git a/test/graph/mail-rules.ts b/test/graph/mail-rules.ts index aa98a6003..2926fb78e 100644 --- a/test/graph/mail-rules.ts +++ b/test/graph/mail-rules.ts @@ -89,6 +89,7 @@ describe("Mail: Rules", function () { return expect(success).to.be.true; }); + // This logs to the console when it passes, ignore those messages it("Mail: Rule Delete", async function () { const r = JSON.parse(JSON.stringify(draftRule)); r.displayName = `PnPjs Test Rule ${getRandomString(8)}`; diff --git a/test/sp/files.ts b/test/sp/files.ts index 62036b643..0adb49c82 100644 --- a/test/sp/files.ts +++ b/test/sp/files.ts @@ -91,6 +91,7 @@ describe("Files", function () { expect(file.Name).to.eq(name); }); + // TODO: This is an ArrayBuffer but the addChunked method doesn't seem to support that when getting a readable stream, needs work. it("addChunked", async function () { const name = `Testing Chunked - ${getRandomString(4)}.jpg`; diff --git a/test/sp/site-groups.ts b/test/sp/site-groups.ts index ed1c3fc31..f98dba0c2 100644 --- a/test/sp/site-groups.ts +++ b/test/sp/site-groups.ts @@ -43,13 +43,20 @@ describe("SiteGroups", function () { }); // requires Custom Scripts to be enabled. Set-PnPSite -Identity -NoScriptSite $false - it("createDefaultAssociatedGroups()", async function () { + // Skipping as "custom scripts" feature disabled as of March 2024 + it.skip("createDefaultAssociatedGroups()", async function () { await this.pnp.sp.web.ensureUser(this.pnp.settings.testUser); const groupName = `TestGroup_${getRandomString(4)}`; - return expect(this.pnp.sp.web.createDefaultAssociatedGroups(groupName, - this.pnp.settings.testUser, - false, - false)).to.be.eventually.fulfilled; + let sucess = true; + try { + await this.pnp.sp.web.createDefaultAssociatedGroups(groupName, + this.pnp.settings.testUser, + false, + false); + } catch (err) { + sucess = false; + } + return expect(sucess).to.be.true; }); }); @@ -57,9 +64,10 @@ describe("SiteGroups", function () { return expect(this.pnp.sp.web.siteGroups.getById(newGroup.Id)()); }); - it("add()", function () { + it("add()", async function () { const newGroupTitle = `test_add_new_sitegroup_${getRandomString(8)}`; - return expect(this.pnp.sp.web.siteGroups.add({ "Title": newGroupTitle })).to.be.eventually.fulfilled; + const newGroup = await this.pnp.sp.web.siteGroups.add({ "Title": newGroupTitle }); + return expect(newGroup.Title).to.equal(newGroupTitle); }); it("getByName()", function () {