Skip to content

Commit

Permalink
Updating caching handlers (#2346)
Browse files Browse the repository at this point in the history
* Fixing e2e for dialog-parent communication

* Making beforeSuspendORTerminateFunction Async

* adding delay to registerBeforeSuspendOrTerminate

* fixing unit tests

* converted contentUrl from string to URL type in resumeContext for cachign handler

* Update @microsoft-teams-js-ec870a0b-890d-4ada-b33b-301c1c28a8d3.json

* nit changefile changes

---------

Co-authored-by: AE ( ͡ಠ ʖ̯ ͡ಠ) <36546967+AE-MS@users.noreply.github.com>
  • Loading branch information
lakhveerk and AE-MS committed Jun 1, 2024
1 parent 1509906 commit 77dc7d6
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 33 deletions.
29 changes: 20 additions & 9 deletions apps/teams-test-app/src/components/AppAPIs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ const RegisterOnResumeHandler = (): React.ReactElement => {
app.lifecycle.registerOnResumeHandler((context: ResumeContext): void => {
setResult('successfully called with context:' + JSON.stringify(context));
// get the route from the context
const route = new URL(context.contentUrl);
const route = context.contentUrl;
// navigate to the correct path based on URL
navigate(route.pathname);
app.notifySuccess();
Expand All @@ -101,18 +101,29 @@ const RegisterOnResumeHandler = (): React.ReactElement => {
};

const RegisterBeforeSuspendOrTerminateHandler = (): React.ReactElement =>
ApiWithoutInput({
ApiWithTextInput<number>({
name: 'RegisterBeforeSuspendOrTerminateHandler',
title: 'Register Before Suspend/Terminate Handler',
onClick: async (setResult) => {
app.lifecycle.registerBeforeSuspendOrTerminateHandler((): void => {
setResult('beforeSuspendOrTerminate received');
});

return 'registered';
onClick: {
validateInput: (input) => {
if (typeof input !== 'number') {
throw new Error('input should be a number');
}
},
submit: async (delay: number, setResult: (result: string) => void) => {
app.lifecycle.registerBeforeSuspendOrTerminateHandler(() => {
return new Promise<void>((resolve) => {
setTimeout(() => {
setResult('beforeSuspendOrTerminate received');
resolve();
}, delay);
});
});
return 'registered';
},
},
defaultInput: '3000',
});

const AppAPIs = (): ReactElement => (
<ModuleWrapper title="App">
<GetContext />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Updated `app.lifecycle.registerBeforeSuspendOrTerminateHandler` to be asynchronous, and updated `app.lifecycle.registerOnResumeHandler` to accept a new Handler type, changing `contentUrl` from `string` to `URL` object.",
"packageName": "@microsoft/teams-js",
"email": "lakhveerkaur@microsoft.com",
"dependentChangeType": "patch"
}
36 changes: 25 additions & 11 deletions packages/teams-js/src/internal/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class HandlersPrivate {
* @deprecated
*/
public static beforeUnloadHandler: null | ((readyToUnload: () => void) => boolean) = null;
public static beforeSuspendOrTerminateHandler: null | (() => void) = null;
public static beforeSuspendOrTerminateHandler: null | (() => Promise<void>) = null;
public static resumeHandler: null | ((context: ResumeContext) => void) = null;

/**
Expand Down Expand Up @@ -197,16 +197,30 @@ export function registerOnLoadHandler(apiVersionTag: string, handler: (context:
* @internal
* Limited to Microsoft-internal use
*/
function handleLoad(context: LoadContext): void {
function handleLoad(loadContext: LoadContext): void {
const resumeContext = convertToResumeContext(loadContext);
if (HandlersPrivate.resumeHandler) {
HandlersPrivate.resumeHandler(context);
HandlersPrivate.resumeHandler(resumeContext);
if (Communication.childWindow) {
sendMessageEventToChild('load', [resumeContext]);
}
} else if (HandlersPrivate.loadHandler) {
HandlersPrivate.loadHandler(context);
HandlersPrivate.loadHandler(loadContext);
if (Communication.childWindow) {
sendMessageEventToChild('load', [loadContext]);
}
}
}

if (Communication.childWindow) {
sendMessageEventToChild('load', [context]);
}
/**
* @internal
* Limited to Microsoft-internal use
*/
function convertToResumeContext(context: LoadContext): ResumeContext {
return {
entityId: context.entityId,
contentUrl: new URL(context.contentUrl),
};
}

/**
Expand All @@ -227,13 +241,13 @@ export function registerBeforeUnloadHandler(
* @internal
* Limited to Microsoft-internal use
*/
function handleBeforeUnload(): void {
async function handleBeforeUnload(): Promise<void> {
const readyToUnload = (): void => {
sendMessageToParent(getApiVersionTag(ApiVersionNumber.V_2, ApiName.HandleBeforeUnload), 'readyToUnload', []);
};

if (HandlersPrivate.beforeSuspendOrTerminateHandler) {
HandlersPrivate.beforeSuspendOrTerminateHandler();
await HandlersPrivate.beforeSuspendOrTerminateHandler();
if (Communication.childWindow) {
sendMessageEventToChild('beforeUnload');
} else {
Expand All @@ -252,7 +266,7 @@ function handleBeforeUnload(): void {
* @internal
* Limited to Microsoft-internal use
*/
export function registerBeforeSuspendOrTerminateHandler(handler: () => void): void {
export function registerBeforeSuspendOrTerminateHandler(handler: () => Promise<void>): void {
HandlersPrivate.beforeSuspendOrTerminateHandler = handler;
!isNullOrUndefined(handler) &&
sendMessageToParent(
Expand All @@ -266,7 +280,7 @@ export function registerBeforeSuspendOrTerminateHandler(handler: () => void): vo
* @internal
* Limited to Microsoft-internal use
*/
export function registerOnResumeHandler(handler: (context: LoadContext) => void): void {
export function registerOnResumeHandler(handler: (context: ResumeContext) => void): void {
HandlersPrivate.resumeHandler = handler;
!isNullOrUndefined(handler) &&
sendMessageToParent(getApiVersionTag(ApiVersionNumber.V_2, ApiName.RegisterOnResumeHandler), 'registerHandler', [
Expand Down
2 changes: 1 addition & 1 deletion packages/teams-js/src/public/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ export namespace app {
*
* @returns void
*/
export type registerBeforeSuspendOrTerminateHandlerFunctionType = () => void;
export type registerBeforeSuspendOrTerminateHandlerFunctionType = () => Promise<void>;

/**
* Registers a handler to be called before the page is suspended or terminated. Once a user navigates away from an app,
Expand Down
14 changes: 12 additions & 2 deletions packages/teams-js/src/public/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -951,14 +951,24 @@ export interface ResumeContext {
/**
* The content URL that is requested to be loaded
*/
contentUrl: string;
contentUrl: URL;
}

/**
* @deprecated
* As of 2.14.1, please use {@link ResumeContext} instead.
*/
export type LoadContext = ResumeContext;
export interface LoadContext {
/**
* The entity that is requested to be loaded
*/
entityId: string;

/**
* The content URL that is requested to be loaded
*/
contentUrl: string;
}

/** Represents information about a frame within a tab or dialog module. */
export interface FrameInfo {
Expand Down
19 changes: 12 additions & 7 deletions packages/teams-js/test/public/app.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
ActionObjectType,
Context,
FileOpenPreference,
LoadContext,
M365ContentAction,
SecondaryM365ContentIdName,
} from '../../src/public/interfaces';
Expand Down Expand Up @@ -43,6 +44,10 @@ function isM365ContentType(actionItem: unknown): actionItem is M365ContentAction

describe('Testing app capability', () => {
const mockErrorMessage = 'Something went wrong...';
const loadContext: LoadContext = {
entityId: 'testEntityId',
contentUrl: 'https://localhost:4000',
};
describe('Framed - Testing app capability', () => {
// Use to send a mock message from the app.
const utils = new Utils();
Expand Down Expand Up @@ -890,7 +895,7 @@ describe('Testing app capability', () => {
describe('Testing app.lifecycle subcapability', () => {
describe('Testing app.lifecycle.registerBeforeSuspendOrTerminateHandler function', () => {
it('should not allow calls before initialization', () => {
expect(() => app.lifecycle.registerBeforeSuspendOrTerminateHandler(() => {})).toThrowError(
expect(() => app.lifecycle.registerBeforeSuspendOrTerminateHandler(async () => {})).toThrowError(
new Error(errorLibraryNotInitialized),
);
});
Expand All @@ -899,7 +904,7 @@ describe('Testing app capability', () => {
it(`app.lifecycle.registerBeforeSuspendOrTerminateHandler should successfully register a beforSuspendOrTerminate handler and readyToUnload should be called. context: ${context}`, async () => {
await utils.initializeWithContext(context);

app.lifecycle.registerBeforeSuspendOrTerminateHandler(() => {});
app.lifecycle.registerBeforeSuspendOrTerminateHandler(async () => {});

await utils.sendMessage('beforeUnload');

Expand All @@ -911,7 +916,7 @@ describe('Testing app capability', () => {
describe('Testing app.lifecycle.registerOnResumeHandler function', () => {
it('should not allow calls before initialization', () => {
expect(() =>
app.lifecycle.registerOnResumeHandler(() => {
app.lifecycle.registerOnResumeHandler(async () => {
return false;
}),
).toThrowError(new Error(errorLibraryNotInitialized));
Expand All @@ -925,8 +930,7 @@ describe('Testing app capability', () => {
app.lifecycle.registerOnResumeHandler(() => {
handlerInvoked = true;
});

await utils.sendMessage('load');
await utils.sendMessage('load', loadContext);
expect(handlerInvoked).toBe(true);
});
});
Expand Down Expand Up @@ -1692,7 +1696,7 @@ describe('Testing app capability', () => {
describe('Testing app.lifecycle subcapability', () => {
describe('Testing app.lifecycle.registerBeforeSuspendOrTerminateHandler function', () => {
it('should not allow calls before initialization', () => {
expect(() => app.lifecycle.registerBeforeSuspendOrTerminateHandler(() => {})).toThrowError(
expect(() => app.lifecycle.registerBeforeSuspendOrTerminateHandler(async () => {})).toThrowError(
new Error(errorLibraryNotInitialized),
);
});
Expand All @@ -1701,7 +1705,7 @@ describe('Testing app capability', () => {
it(`app.lifecycle.registerBeforeSuspendOrTerminateHandler should successfully register a beforSuspendOrTerminate handler and readyToUnload should be called. context: ${context}`, async () => {
await utils.initializeWithContext(context);

app.lifecycle.registerBeforeSuspendOrTerminateHandler(() => {});
app.lifecycle.registerBeforeSuspendOrTerminateHandler(async () => {});
await utils.respondToFramelessMessage({
data: {
func: 'beforeUnload',
Expand Down Expand Up @@ -1733,6 +1737,7 @@ describe('Testing app capability', () => {
await utils.respondToFramelessMessage({
data: {
func: 'load',
args: [loadContext],
},
} as DOMMessageEvent);
expect(handlerInvoked).toBe(true);
Expand Down
9 changes: 7 additions & 2 deletions packages/teams-js/test/public/publicAPIs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as utilFunc from '../../src/internal/utils';
import { app } from '../../src/public';
import { HostClientType, TeamType, UserTeamRole } from '../../src/public/constants';
import { FrameContexts } from '../../src/public/constants';
import { Context, FrameContext, TabInstanceParameters } from '../../src/public/interfaces';
import { Context, FrameContext, LoadContext, TabInstanceParameters } from '../../src/public/interfaces';
import * as microsoftTeams from '../../src/public/publicAPIs';
import {
enablePrintCapability,
Expand Down Expand Up @@ -849,7 +849,12 @@ describe('MicrosoftTeams-publicAPIs', () => {
return false;
});

await utils.sendMessage('load');
const loadContext: LoadContext = {
entityId: 'testEntityId',
contentUrl: 'https://localhost:4000',
};

await utils.sendMessage('load', loadContext);

expect(handlerInvoked).toBe(true);
});
Expand Down
8 changes: 7 additions & 1 deletion packages/teams-js/test/public/teamsAPIs.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { errorLibraryNotInitialized } from '../../src/internal/constants';
import { LoadContext } from '../../src/public';
import { app } from '../../src/public/app';
import { errorNotSupportedOnPlatform, FrameContexts } from '../../src/public/constants';
import { _minRuntimeConfigToUninitialize } from '../../src/public/runtime';
Expand Down Expand Up @@ -32,6 +33,11 @@ describe('Testing TeamsCore Capability', () => {
}
});

const loadContext: LoadContext = {
entityId: 'testEntityId',
contentUrl: 'https://localhost:4000',
};

describe('Testing teamsCore.isSupported function', () => {
it('should throw if called before initialization', () => {
utils.uninitializeRuntimeConfig();
Expand Down Expand Up @@ -164,7 +170,7 @@ describe('Testing TeamsCore Capability', () => {
return false;
});

await utils.sendMessage('load');
await utils.sendMessage('load', loadContext);

expect(handlerInvoked).toBe(true);
});
Expand Down

0 comments on commit 77dc7d6

Please sign in to comment.