From 9e0864ad78dd100fc9b9d1d73a3836428e7e0a9e Mon Sep 17 00:00:00 2001 From: Yuri Dogandjiev Date: Thu, 20 Sep 2018 14:56:53 -0700 Subject: [PATCH] Allow authenticate calls from the task module frame context --- src/MicrosoftTeams.ts | 6 +- test/MicrosoftTeams.spec.ts | 905 ++++++++++++++++++++---------------- 2 files changed, 517 insertions(+), 394 deletions(-) diff --git a/src/MicrosoftTeams.ts b/src/MicrosoftTeams.ts index 3516c5a663..363a0460d1 100644 --- a/src/MicrosoftTeams.ts +++ b/src/MicrosoftTeams.ts @@ -687,7 +687,8 @@ namespace microsoftTeams { ensureInitialized( frameContexts.content, frameContexts.settings, - frameContexts.remove + frameContexts.remove, + frameContexts.task ); let messageId = sendMessageRequest(parentWindow, "navigateCrossDomain", [ @@ -1107,7 +1108,8 @@ namespace microsoftTeams { ensureInitialized( frameContexts.content, frameContexts.settings, - frameContexts.remove + frameContexts.remove, + frameContexts.task ); if (hostClientType === HostClientType.desktop) { diff --git a/test/MicrosoftTeams.spec.ts b/test/MicrosoftTeams.spec.ts index d4b888586a..6a1c1e76f0 100644 --- a/test/MicrosoftTeams.spec.ts +++ b/test/MicrosoftTeams.spec.ts @@ -463,36 +463,6 @@ describe("MicrosoftTeams", () => { expect(handlerInvoked).toBe(true); }); - it("should successfully navigate cross-origin", () => { - initializeWithContext("content"); - - microsoftTeams.navigateCrossDomain("https://valid.origin.com"); - - let navigateCrossDomainMessage = findMessageByFunc("navigateCrossDomain"); - expect(navigateCrossDomainMessage).not.toBeNull(); - expect(navigateCrossDomainMessage.args.length).toBe(1); - expect(navigateCrossDomainMessage.args[0]).toBe("https://valid.origin.com"); - }); - - it("should throw on invalid cross-origin navigation request", () => { - initializeWithContext("settings"); - - microsoftTeams.navigateCrossDomain("https://invalid.origin.com"); - - let navigateCrossDomainMessage = findMessageByFunc("navigateCrossDomain"); - expect(navigateCrossDomainMessage).not.toBeNull(); - expect(navigateCrossDomainMessage.args.length).toBe(1); - expect(navigateCrossDomainMessage.args[0]).toBe( - "https://invalid.origin.com" - ); - - let respondWithFailure = () => { - respondToMessage(navigateCrossDomainMessage, false); - }; - - expect(respondWithFailure).toThrow(); - }); - it("should successfully set validity state to true", () => { initializeWithContext("settings"); @@ -730,442 +700,593 @@ describe("MicrosoftTeams", () => { expect(message.args.length).toBe(0); }); - it("should successfully pop up the auth window", () => { + it("should successfully share a deep link", () => { initializeWithContext("content"); - let windowOpenCalled = false; - spyOn(microsoftTeams._window, "open").and.callFake( - (url: string, name: string, specs: string): Window => { - expect(url).toEqual("https://someurl/"); - expect(name).toEqual("_blank"); - expect(specs.indexOf("width=100")).not.toBe(-1); - expect(specs.indexOf("height=200")).not.toBe(-1); - windowOpenCalled = true; - return childWindow as Window; - } - ); + microsoftTeams.shareDeepLink({ + subEntityId: "someSubEntityId", + subEntityLabel: "someSubEntityLabel", + subEntityWebUrl: "someSubEntityWebUrl" + }); - let authenticationParams = { - url: "https://someurl/", - width: 100, - height: 200 - }; - microsoftTeams.authentication.authenticate(authenticationParams); - expect(windowOpenCalled).toBe(true); + let message = findMessageByFunc("shareDeepLink"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(3); + expect(message.args[0]).toBe("someSubEntityId"); + expect(message.args[1]).toBe("someSubEntityLabel"); + expect(message.args[2]).toBe("someSubEntityWebUrl"); }); - it("should successfully pop up the auth window when authenticate called without authenticationParams for connectors", () => { + it("should successfully open a file preview", () => { initializeWithContext("content"); - let windowOpenCalled = false; - spyOn(microsoftTeams._window, "open").and.callFake( - (url: string, name: string, specs: string): Window => { - expect(url).toEqual("https://someurl/"); - expect(name).toEqual("_blank"); - expect(specs.indexOf("width=100")).not.toBe(-1); - expect(specs.indexOf("height=200")).not.toBe(-1); - windowOpenCalled = true; - return childWindow as Window; - } - ); + microsoftTeams.openFilePreview({ + entityId: "someEntityId", + title: "someTitle", + description: "someDescription", + type: "someType", + objectUrl: "someObjectUrl", + downloadUrl: "someDownloadUrl", + webPreviewUrl: "someWebPreviewUrl", + webEditUrl: "someWebEditUrl", + baseUrl: "someBaseUrl", + editFile: true, + subEntityId: "someSubEntityId" + }); - let authenticationParams = { - url: "https://someurl/", - width: 100, - height: 200 - }; - microsoftTeams.authentication.registerAuthenticationHandlers( - authenticationParams - ); - microsoftTeams.authentication.authenticate(); - expect(windowOpenCalled).toBe(true); + let message = findMessageByFunc("openFilePreview"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(11); + expect(message.args[0]).toBe("someEntityId"); + expect(message.args[1]).toBe("someTitle"); + expect(message.args[2]).toBe("someDescription"); + expect(message.args[3]).toBe("someType"); + expect(message.args[4]).toBe("someObjectUrl"); + expect(message.args[5]).toBe("someDownloadUrl"); + expect(message.args[6]).toBe("someWebPreviewUrl"); + expect(message.args[7]).toBe("someWebEditUrl"); + expect(message.args[8]).toBe("someBaseUrl"); + expect(message.args[9]).toBe(true); + expect(message.args[10]).toBe("someSubEntityId"); }); - it("should cancel the flow when the auth window gets closed before notifySuccess/notifyFailure are called", () => { - initializeWithContext("content"); + describe("navigateCrossDomain", () => { + it("should not allow calls before initialization", () => { + expect(() => + microsoftTeams.navigateCrossDomain("https://valid.origin.com") + ).toThrowError("The library has not yet been initialized"); + }); - let windowOpenCalled = false; - spyOn(microsoftTeams._window, "open").and.callFake( - (url: string, name: string, specs: string): Window => { - expect(url).toEqual("https://someurl/"); - expect(name).toEqual("_blank"); - expect(specs.indexOf("width=100")).not.toBe(-1); - expect(specs.indexOf("height=200")).not.toBe(-1); - windowOpenCalled = true; - return childWindow as Window; - } - ); + it("should not allow calls from authentication context", () => { + initializeWithContext("authentication"); - let successResult: string; - let failureReason: string; - let authenticationParams = { - url: "https://someurl/", - width: 100, - height: 200, - successCallback: (result: string) => (successResult = result), - failureCallback: (reason: string) => (failureReason = reason) - }; - microsoftTeams.authentication.authenticate(authenticationParams); - expect(windowOpenCalled).toBe(true); + expect(() => + microsoftTeams.navigateCrossDomain("https://valid.origin.com") + ).toThrowError( + "This call is not allowed in the 'authentication' context" + ); + }); - childWindow.closed = true; - jasmine.clock().tick(101); + it("should allow calls from content context", () => { + initializeWithContext("content"); - expect(successResult).toBeUndefined(); - expect(failureReason).toEqual("CancelledByUser"); - }); + microsoftTeams.navigateCrossDomain("https://valid.origin.com"); + }); - it("should successfully handle auth success", () => { - initializeWithContext("content"); + it("should allow calls from settings context", () => { + initializeWithContext("settings"); - let successResult: string; - let failureReason: string; - let authenticationParams = { - url: "https://someurl/", - width: 100, - height: 200, - successCallback: (result: string) => (successResult = result), - failureCallback: (reason: string) => (failureReason = reason) - }; - microsoftTeams.authentication.authenticate(authenticationParams); + microsoftTeams.navigateCrossDomain("https://valid.origin.com"); + }); - processMessage({ - origin: tabOrigin, - source: childWindow, - data: { - id: 0, - func: "authentication.authenticate.success", - args: ["someResult"] - } - } as MessageEvent); + it("should allow calls from remove context", () => { + initializeWithContext("remove"); - expect(successResult).toEqual("someResult"); - expect(failureReason).toBeUndefined(); - }); + microsoftTeams.navigateCrossDomain("https://valid.origin.com"); + }); - it("should successfully handle auth failure", () => { - initializeWithContext("content"); + it("should allow calls from task context", () => { + initializeWithContext("task"); - let successResult: string; - let failureReason: string; - let authenticationParams = { - url: "https://someurl/", - width: 100, - height: 200, - successCallback: (result: string) => (successResult = result), - failureCallback: (reason: string) => (failureReason = reason) - }; - microsoftTeams.authentication.authenticate(authenticationParams); + microsoftTeams.navigateCrossDomain("https://valid.origin.com"); + }); - processMessage({ - origin: tabOrigin, - source: childWindow, - data: { - id: 0, - func: "authentication.authenticate.failure", - args: ["someReason"] - } - } as MessageEvent); + it("should successfully navigate cross-origin", () => { + initializeWithContext("content"); - expect(successResult).toBeUndefined(); - expect(failureReason).toEqual("someReason"); - }); + microsoftTeams.navigateCrossDomain("https://valid.origin.com"); - it("should successfully pop up the auth window in the desktop client", () => { - initializeWithContext("content", "desktop"); + let navigateCrossDomainMessage = findMessageByFunc("navigateCrossDomain"); + expect(navigateCrossDomainMessage).not.toBeNull(); + expect(navigateCrossDomainMessage.args.length).toBe(1); + expect(navigateCrossDomainMessage.args[0]).toBe( + "https://valid.origin.com" + ); + }); - let authenticationParams = { - url: "https://someUrl", - width: 100, - height: 200 - }; - microsoftTeams.authentication.authenticate(authenticationParams); + it("should throw on invalid cross-origin navigation request", () => { + initializeWithContext("settings"); - let message = findMessageByFunc("authentication.authenticate"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(3); - expect(message.args[0]).toBe(authenticationParams.url.toLowerCase() + "/"); - expect(message.args[1]).toBe(authenticationParams.width); - expect(message.args[2]).toBe(authenticationParams.height); + microsoftTeams.navigateCrossDomain("https://invalid.origin.com"); + + let navigateCrossDomainMessage = findMessageByFunc("navigateCrossDomain"); + expect(navigateCrossDomainMessage).not.toBeNull(); + expect(navigateCrossDomainMessage.args.length).toBe(1); + expect(navigateCrossDomainMessage.args[0]).toBe( + "https://invalid.origin.com" + ); + + let respondWithFailure = () => { + respondToMessage(navigateCrossDomainMessage, false); + }; + + expect(respondWithFailure).toThrow(); + }); }); - it("should successfully handle auth success in the desktop client", () => { - initializeWithContext("content", "desktop"); - - let successResult: string; - let failureReason: string; - let authenticationParams = { - url: "https://someUrl", - width: 100, - height: 200, - successCallback: (result: string) => (successResult = result), - failureCallback: (reason: string) => (failureReason = reason) - }; - microsoftTeams.authentication.authenticate(authenticationParams); + describe("authentication", () => { + it("should not allow authentication.authenticate calls before initialization", () => { + const authenticationParams: microsoftTeams.authentication.AuthenticateParameters = { + url: "https://someurl/", + width: 100, + height: 200 + }; - let message = findMessageByFunc("authentication.authenticate"); - expect(message).not.toBeNull(); + expect(() => + microsoftTeams.authentication.authenticate(authenticationParams) + ).toThrowError("The library has not yet been initialized"); + }); - respondToMessage(message, true, "someResult"); + it("should not allow authentication.authenticate calls from authentication context", () => { + initializeWithContext("authentication"); - expect(successResult).toBe("someResult"); - expect(failureReason).toBeUndefined(); - }); + const authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; - it("should successfully handle auth failure in the desktop client", () => { - initializeWithContext("content", "desktop"); - - let successResult: string; - let failureReason: string; - let authenticationParams = { - url: "https://someUrl", - width: 100, - height: 200, - successCallback: (result: string) => (successResult = result), - failureCallback: (reason: string) => (failureReason = reason) - }; - microsoftTeams.authentication.authenticate(authenticationParams); + const taskInfo: microsoftTeams.TaskInfo = {}; + expect(() => + microsoftTeams.authentication.authenticate(authenticationParams) + ).toThrowError( + "This call is not allowed in the 'authentication' context" + ); + }); - let message = findMessageByFunc("authentication.authenticate"); - expect(message).not.toBeNull(); + it("should allow authentication.authenticate calls from content context", () => { + initializeWithContext("content"); - respondToMessage(message, false, "someReason"); + const authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; + microsoftTeams.authentication.authenticate(authenticationParams); + }); - expect(successResult).toBeUndefined(); - expect(failureReason).toBe("someReason"); - }); + it("should allow authentication.authenticate calls from settings context", () => { + initializeWithContext("settings"); - it("should successfully notify auth success", () => { - initializeWithContext("authentication"); + const authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; + microsoftTeams.authentication.authenticate(authenticationParams); + }); - microsoftTeams.authentication.notifySuccess("someResult"); - let message = findMessageByFunc("authentication.authenticate.success"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(1); - expect(message.args[0]).toBe("someResult"); - }); + it("should allow authentication.authenticate calls from remove context", () => { + initializeWithContext("remove"); - it("should do window redirect if callbackUrl is for win32 Outlook", () => { - let windowAssignSpyCalled = false; - spyOn(microsoftTeams._window.location, "assign").and.callFake( - (url: string): void => { - windowAssignSpyCalled = true; - expect(url).toEqual( - "https://outlook.office.com/connectors?client_type=Win32_Outlook#/configurations&result=someResult&authSuccess" - ); - } - ); + const authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; + microsoftTeams.authentication.authenticate(authenticationParams); + }); - initializeWithContext("authentication"); + it("should allow authentication.authenticate calls from task context", () => { + initializeWithContext("task"); - microsoftTeams.authentication.notifySuccess( - "someResult", - "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook%23%2Fconfigurations" - ); - expect(windowAssignSpyCalled).toBe(true); - }); + const authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; + microsoftTeams.authentication.authenticate(authenticationParams); + }); - it("should do window redirect if callbackUrl is for win32 Outlook and no result param specified", () => { - let windowAssignSpyCalled = false; - spyOn(microsoftTeams._window.location, "assign").and.callFake( - (url: string): void => { - windowAssignSpyCalled = true; - expect(url).toEqual( - "https://outlook.office.com/connectors?client_type=Win32_Outlook#/configurations&authSuccess" - ); - } - ); + it("should successfully pop up the auth window", () => { + initializeWithContext("content"); - initializeWithContext("authentication"); + let windowOpenCalled = false; + spyOn(microsoftTeams._window, "open").and.callFake( + (url: string, name: string, specs: string): Window => { + expect(url).toEqual("https://someurl/"); + expect(name).toEqual("_blank"); + expect(specs.indexOf("width=100")).not.toBe(-1); + expect(specs.indexOf("height=200")).not.toBe(-1); + windowOpenCalled = true; + return childWindow as Window; + } + ); - microsoftTeams.authentication.notifySuccess( - null, - "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook%23%2Fconfigurations" - ); - expect(windowAssignSpyCalled).toBe(true); - }); + let authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; + microsoftTeams.authentication.authenticate(authenticationParams); + expect(windowOpenCalled).toBe(true); + }); - it("should do window redirect if callbackUrl is for win32 Outlook but does not have URL fragments", () => { - let windowAssignSpyCalled = false; - spyOn(microsoftTeams._window.location, "assign").and.callFake( - (url: string): void => { - windowAssignSpyCalled = true; - expect(url).toEqual( - "https://outlook.office.com/connectors?client_type=Win32_Outlook#&result=someResult&authSuccess" - ); - } - ); + it("should successfully pop up the auth window when authenticate called without authenticationParams for connectors", () => { + initializeWithContext("content"); - initializeWithContext("authentication"); + let windowOpenCalled = false; + spyOn(microsoftTeams._window, "open").and.callFake( + (url: string, name: string, specs: string): Window => { + expect(url).toEqual("https://someurl/"); + expect(name).toEqual("_blank"); + expect(specs.indexOf("width=100")).not.toBe(-1); + expect(specs.indexOf("height=200")).not.toBe(-1); + windowOpenCalled = true; + return childWindow as Window; + } + ); - microsoftTeams.authentication.notifySuccess( - "someResult", - "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook" - ); - expect(windowAssignSpyCalled).toBe(true); - }); + let authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200 + }; + microsoftTeams.authentication.registerAuthenticationHandlers( + authenticationParams + ); + microsoftTeams.authentication.authenticate(); + expect(windowOpenCalled).toBe(true); + }); - it("should successfully notify auth success if callbackUrl is not for win32 Outlook", () => { - initializeWithContext("authentication"); + it("should cancel the flow when the auth window gets closed before notifySuccess/notifyFailure are called", () => { + initializeWithContext("content"); - microsoftTeams.authentication.notifySuccess( - "someResult", - "https%3A%2F%2Fsomeinvalidurl.com%3FcallbackUrl%3Dtest%23%2Fconfiguration" - ); - let message = findMessageByFunc("authentication.authenticate.success"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(1); - expect(message.args[0]).toBe("someResult"); - }); + let windowOpenCalled = false; + spyOn(microsoftTeams._window, "open").and.callFake( + (url: string, name: string, specs: string): Window => { + expect(url).toEqual("https://someurl/"); + expect(name).toEqual("_blank"); + expect(specs.indexOf("width=100")).not.toBe(-1); + expect(specs.indexOf("height=200")).not.toBe(-1); + windowOpenCalled = true; + return childWindow as Window; + } + ); - it("should successfully notify auth failure", () => { - initializeWithContext("authentication"); + let successResult: string; + let failureReason: string; + let authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200, + successCallback: (result: string) => (successResult = result), + failureCallback: (reason: string) => (failureReason = reason) + }; + microsoftTeams.authentication.authenticate(authenticationParams); + expect(windowOpenCalled).toBe(true); - microsoftTeams.authentication.notifyFailure("someReason"); + childWindow.closed = true; + jasmine.clock().tick(101); - let message = findMessageByFunc("authentication.authenticate.failure"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(1); - expect(message.args[0]).toBe("someReason"); - }); + expect(successResult).toBeUndefined(); + expect(failureReason).toEqual("CancelledByUser"); + }); - it("should do window redirect if callbackUrl is for win32 Outlook and auth failure happens", () => { - let windowAssignSpyCalled = false; - spyOn(microsoftTeams._window.location, "assign").and.callFake( - (url: string): void => { - windowAssignSpyCalled = true; - expect(url).toEqual( - "https://outlook.office.com/connectors?client_type=Win32_Outlook#/configurations&reason=someReason&authFailure" - ); - } - ); + it("should successfully handle auth success", () => { + initializeWithContext("content"); - initializeWithContext("authentication"); + let successResult: string; + let failureReason: string; + let authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200, + successCallback: (result: string) => (successResult = result), + failureCallback: (reason: string) => (failureReason = reason) + }; + microsoftTeams.authentication.authenticate(authenticationParams); - microsoftTeams.authentication.notifyFailure( - "someReason", - "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook%23%2Fconfigurations" - ); - expect(windowAssignSpyCalled).toBe(true); - }); + processMessage({ + origin: tabOrigin, + source: childWindow, + data: { + id: 0, + func: "authentication.authenticate.success", + args: ["someResult"] + } + } as MessageEvent); - it("should successfully notify auth failure if callbackUrl is not for win32 Outlook", () => { - spyOn(microsoftTeams._window.location, "assign").and.callFake( - (url: string): void => { - expect(url).toEqual( - "https://someinvalidurl.com?callbackUrl=test#/configuration&reason=someReason&authFailure" - ); - } - ); + expect(successResult).toEqual("someResult"); + expect(failureReason).toBeUndefined(); + }); - initializeWithContext("authentication"); + it("should successfully handle auth failure", () => { + initializeWithContext("content"); - microsoftTeams.authentication.notifyFailure( - "someReason", - "https%3A%2F%2Fsomeinvalidurl.com%3FcallbackUrl%3Dtest%23%2Fconfiguration" - ); - let message = findMessageByFunc("authentication.authenticate.failure"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(1); - expect(message.args[0]).toBe("someReason"); - }); + let successResult: string; + let failureReason: string; + let authenticationParams = { + url: "https://someurl/", + width: 100, + height: 200, + successCallback: (result: string) => (successResult = result), + failureCallback: (reason: string) => (failureReason = reason) + }; + microsoftTeams.authentication.authenticate(authenticationParams); - it("should not close auth window before notify success message has been sent", () => { - let closeWindowSpy = spyOn( - microsoftTeams._window, - "close" - ).and.callThrough(); + processMessage({ + origin: tabOrigin, + source: childWindow, + data: { + id: 0, + func: "authentication.authenticate.failure", + args: ["someReason"] + } + } as MessageEvent); - microsoftTeams.initialize(); - let initMessage = findMessageByFunc("initialize"); - expect(initMessage).not.toBeNull(); + expect(successResult).toBeUndefined(); + expect(failureReason).toEqual("someReason"); + }); - microsoftTeams.authentication.notifySuccess("someResult"); - let message = findMessageByFunc("authentication.authenticate.success"); - expect(message).toBeNull(); - expect(closeWindowSpy).not.toHaveBeenCalled(); + it("should successfully pop up the auth window in the desktop client", () => { + initializeWithContext("content", "desktop"); - respondToMessage(initMessage, "authentication"); - message = findMessageByFunc("authentication.authenticate.success"); - expect(message).not.toBeNull(); + let authenticationParams = { + url: "https://someUrl", + width: 100, + height: 200 + }; + microsoftTeams.authentication.authenticate(authenticationParams); - // Wait 100ms for the message queue and 200ms for the close delay - jasmine.clock().tick(301); - expect(closeWindowSpy).toHaveBeenCalled(); - }); + let message = findMessageByFunc("authentication.authenticate"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(3); + expect(message.args[0]).toBe( + authenticationParams.url.toLowerCase() + "/" + ); + expect(message.args[1]).toBe(authenticationParams.width); + expect(message.args[2]).toBe(authenticationParams.height); + }); - it("should not close auth window before notify failure message has been sent", () => { - let closeWindowSpy = spyOn( - microsoftTeams._window, - "close" - ).and.callThrough(); + it("should successfully handle auth success in the desktop client", () => { + initializeWithContext("content", "desktop"); + + let successResult: string; + let failureReason: string; + let authenticationParams = { + url: "https://someUrl", + width: 100, + height: 200, + successCallback: (result: string) => (successResult = result), + failureCallback: (reason: string) => (failureReason = reason) + }; + microsoftTeams.authentication.authenticate(authenticationParams); - microsoftTeams.initialize(); - let initMessage = findMessageByFunc("initialize"); - expect(initMessage).not.toBeNull(); + let message = findMessageByFunc("authentication.authenticate"); + expect(message).not.toBeNull(); - microsoftTeams.authentication.notifyFailure("someReason"); - let message = findMessageByFunc("authentication.authenticate.failure"); - expect(message).toBeNull(); - expect(closeWindowSpy).not.toHaveBeenCalled(); + respondToMessage(message, true, "someResult"); - respondToMessage(initMessage, "authentication"); - message = findMessageByFunc("authentication.authenticate.failure"); - expect(message).not.toBeNull(); + expect(successResult).toBe("someResult"); + expect(failureReason).toBeUndefined(); + }); - // Wait 100ms for the message queue and 200ms for the close delay - jasmine.clock().tick(301); - expect(closeWindowSpy).toHaveBeenCalled(); - }); + it("should successfully handle auth failure in the desktop client", () => { + initializeWithContext("content", "desktop"); + + let successResult: string; + let failureReason: string; + let authenticationParams = { + url: "https://someUrl", + width: 100, + height: 200, + successCallback: (result: string) => (successResult = result), + failureCallback: (reason: string) => (failureReason = reason) + }; + microsoftTeams.authentication.authenticate(authenticationParams); - it("should successfully share a deep link", () => { - initializeWithContext("content"); + let message = findMessageByFunc("authentication.authenticate"); + expect(message).not.toBeNull(); - microsoftTeams.shareDeepLink({ - subEntityId: "someSubEntityId", - subEntityLabel: "someSubEntityLabel", - subEntityWebUrl: "someSubEntityWebUrl" + respondToMessage(message, false, "someReason"); + + expect(successResult).toBeUndefined(); + expect(failureReason).toBe("someReason"); }); - let message = findMessageByFunc("shareDeepLink"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(3); - expect(message.args[0]).toBe("someSubEntityId"); - expect(message.args[1]).toBe("someSubEntityLabel"); - expect(message.args[2]).toBe("someSubEntityWebUrl"); - }); + it("should successfully notify auth success", () => { + initializeWithContext("authentication"); - it("should successfully open a file preview", () => { - initializeWithContext("content"); + microsoftTeams.authentication.notifySuccess("someResult"); + let message = findMessageByFunc("authentication.authenticate.success"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(1); + expect(message.args[0]).toBe("someResult"); + }); - microsoftTeams.openFilePreview({ - entityId: "someEntityId", - title: "someTitle", - description: "someDescription", - type: "someType", - objectUrl: "someObjectUrl", - downloadUrl: "someDownloadUrl", - webPreviewUrl: "someWebPreviewUrl", - webEditUrl: "someWebEditUrl", - baseUrl: "someBaseUrl", - editFile: true, - subEntityId: "someSubEntityId" + it("should do window redirect if callbackUrl is for win32 Outlook", () => { + let windowAssignSpyCalled = false; + spyOn(microsoftTeams._window.location, "assign").and.callFake( + (url: string): void => { + windowAssignSpyCalled = true; + expect(url).toEqual( + "https://outlook.office.com/connectors?client_type=Win32_Outlook#/configurations&result=someResult&authSuccess" + ); + } + ); + + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifySuccess( + "someResult", + "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook%23%2Fconfigurations" + ); + expect(windowAssignSpyCalled).toBe(true); }); - let message = findMessageByFunc("openFilePreview"); - expect(message).not.toBeNull(); - expect(message.args.length).toBe(11); - expect(message.args[0]).toBe("someEntityId"); - expect(message.args[1]).toBe("someTitle"); - expect(message.args[2]).toBe("someDescription"); - expect(message.args[3]).toBe("someType"); - expect(message.args[4]).toBe("someObjectUrl"); - expect(message.args[5]).toBe("someDownloadUrl"); - expect(message.args[6]).toBe("someWebPreviewUrl"); - expect(message.args[7]).toBe("someWebEditUrl"); - expect(message.args[8]).toBe("someBaseUrl"); - expect(message.args[9]).toBe(true); - expect(message.args[10]).toBe("someSubEntityId"); + it("should do window redirect if callbackUrl is for win32 Outlook and no result param specified", () => { + let windowAssignSpyCalled = false; + spyOn(microsoftTeams._window.location, "assign").and.callFake( + (url: string): void => { + windowAssignSpyCalled = true; + expect(url).toEqual( + "https://outlook.office.com/connectors?client_type=Win32_Outlook#/configurations&authSuccess" + ); + } + ); + + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifySuccess( + null, + "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook%23%2Fconfigurations" + ); + expect(windowAssignSpyCalled).toBe(true); + }); + + it("should do window redirect if callbackUrl is for win32 Outlook but does not have URL fragments", () => { + let windowAssignSpyCalled = false; + spyOn(microsoftTeams._window.location, "assign").and.callFake( + (url: string): void => { + windowAssignSpyCalled = true; + expect(url).toEqual( + "https://outlook.office.com/connectors?client_type=Win32_Outlook#&result=someResult&authSuccess" + ); + } + ); + + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifySuccess( + "someResult", + "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook" + ); + expect(windowAssignSpyCalled).toBe(true); + }); + + it("should successfully notify auth success if callbackUrl is not for win32 Outlook", () => { + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifySuccess( + "someResult", + "https%3A%2F%2Fsomeinvalidurl.com%3FcallbackUrl%3Dtest%23%2Fconfiguration" + ); + let message = findMessageByFunc("authentication.authenticate.success"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(1); + expect(message.args[0]).toBe("someResult"); + }); + + it("should successfully notify auth failure", () => { + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifyFailure("someReason"); + + let message = findMessageByFunc("authentication.authenticate.failure"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(1); + expect(message.args[0]).toBe("someReason"); + }); + + it("should do window redirect if callbackUrl is for win32 Outlook and auth failure happens", () => { + let windowAssignSpyCalled = false; + spyOn(microsoftTeams._window.location, "assign").and.callFake( + (url: string): void => { + windowAssignSpyCalled = true; + expect(url).toEqual( + "https://outlook.office.com/connectors?client_type=Win32_Outlook#/configurations&reason=someReason&authFailure" + ); + } + ); + + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifyFailure( + "someReason", + "https%3A%2F%2Foutlook.office.com%2Fconnectors%3Fclient_type%3DWin32_Outlook%23%2Fconfigurations" + ); + expect(windowAssignSpyCalled).toBe(true); + }); + + it("should successfully notify auth failure if callbackUrl is not for win32 Outlook", () => { + spyOn(microsoftTeams._window.location, "assign").and.callFake( + (url: string): void => { + expect(url).toEqual( + "https://someinvalidurl.com?callbackUrl=test#/configuration&reason=someReason&authFailure" + ); + } + ); + + initializeWithContext("authentication"); + + microsoftTeams.authentication.notifyFailure( + "someReason", + "https%3A%2F%2Fsomeinvalidurl.com%3FcallbackUrl%3Dtest%23%2Fconfiguration" + ); + let message = findMessageByFunc("authentication.authenticate.failure"); + expect(message).not.toBeNull(); + expect(message.args.length).toBe(1); + expect(message.args[0]).toBe("someReason"); + }); + + it("should not close auth window before notify success message has been sent", () => { + let closeWindowSpy = spyOn( + microsoftTeams._window, + "close" + ).and.callThrough(); + + microsoftTeams.initialize(); + let initMessage = findMessageByFunc("initialize"); + expect(initMessage).not.toBeNull(); + + microsoftTeams.authentication.notifySuccess("someResult"); + let message = findMessageByFunc("authentication.authenticate.success"); + expect(message).toBeNull(); + expect(closeWindowSpy).not.toHaveBeenCalled(); + + respondToMessage(initMessage, "authentication"); + message = findMessageByFunc("authentication.authenticate.success"); + expect(message).not.toBeNull(); + + // Wait 100ms for the message queue and 200ms for the close delay + jasmine.clock().tick(301); + expect(closeWindowSpy).toHaveBeenCalled(); + }); + + it("should not close auth window before notify failure message has been sent", () => { + let closeWindowSpy = spyOn( + microsoftTeams._window, + "close" + ).and.callThrough(); + + microsoftTeams.initialize(); + let initMessage = findMessageByFunc("initialize"); + expect(initMessage).not.toBeNull(); + + microsoftTeams.authentication.notifyFailure("someReason"); + let message = findMessageByFunc("authentication.authenticate.failure"); + expect(message).toBeNull(); + expect(closeWindowSpy).not.toHaveBeenCalled(); + + respondToMessage(initMessage, "authentication"); + message = findMessageByFunc("authentication.authenticate.failure"); + expect(message).not.toBeNull(); + + // Wait 100ms for the message queue and 200ms for the close delay + jasmine.clock().tick(301); + expect(closeWindowSpy).toHaveBeenCalled(); + }); }); describe("getTabInstances", () => {