Skip to content
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

[TEST CAPABILITY] breaking change example #2625

Draft
wants to merge 1 commit into
base: alexneyman/fb-testing-backwards
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion apps/teams-test-app/e2e-test-data/presence.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{
"feature": {
"id": "presence",
"version": 1
"version": 2
},
"testCases": [
{
Expand Down Expand Up @@ -74,6 +74,40 @@
"upn": " "
},
"expectedTestAppValue": "Error: Error: Error code: 4000, message: UPN cannot be null or empty"
},
{
"title": "setPresence API Call - Set Out of Office",
"type": "callResponse",
"boxSelector": "#box_setPresence",
"inputValue": {
"status": "OutOfOffice",
"customMessage": "On vacation",
"outOfOfficeDetails": {
"startTime": "2024-03-20T00:00:00Z",
"endTime": "2024-03-27T00:00:00Z",
"message": "Annual leave - back next week"
}
},
"expectedAlertValue": "setPresence called with status: OutOfOffice and message: On vacation\nOOF from 3/20/2024 to 3/27/2024",
"expectedTestAppValue": "Presence set successfully"
},
{
"title": "getPresence API Call - Get OOF User",
"type": "callResponse",
"boxSelector": "#box_getPresence",
"inputValue": {
"upn": "oof.user@contoso.com"
},
"expectedAlertValue": "getPresence called for user: oof.user@contoso.com",
"expectedTestAppValue": {
"status": "OutOfOffice",
"customMessage": "Mock presence status",
"outOfOfficeDetails": {
"startTime": "__ANY_STRING__",
"endTime": "__ANY_STRING__",
"message": "On vacation until next week"
}
}
}
]
}
Expand Down
115 changes: 114 additions & 1 deletion packages/teams-js/src/public/presence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,31 @@ export enum PresenceStatus {
* User is offline and cannot be contacted
*/
Offline = 'Offline',

/**
* User is out of office
*/
OutOfOffice = 'OutOfOffice',
}

/**
* Out of office details for a user
*/
export interface OutOfOfficeDetails {
/**
* Start time of OOF period (ISO string)
*/
startTime: string;

/**
* End time of OOF period (ISO string)
*/
endTime: string;

/**
* OOF message to display
*/
message: string;
}

/**
Expand All @@ -55,6 +80,12 @@ export interface UserPresence {
* Optional custom status message
*/
customMessage?: string;

/**
* Optional out of office details
* Only present when status is OutOfOffice
*/
outOfOfficeDetails?: OutOfOfficeDetails;
}

/**
Expand All @@ -80,14 +111,44 @@ export interface SetPresenceParams {
* Optional custom status message
*/
customMessage?: string;

/**
* Optional out of office details
* Only valid when status is OutOfOffice
*/
outOfOfficeDetails?: OutOfOfficeDetails;
}

/**
* Response handler for presence information
*/
class UserPresenceResponseHandler extends ResponseHandler<UserPresence, UserPresence> {
public validate(response: UserPresence): boolean {
return response !== undefined && Object.values(PresenceStatus).includes(response.status);
if (response === undefined || !Object.values(PresenceStatus).includes(response.status)) {
return false;
}

// Validate OOF details if present
if (response.outOfOfficeDetails) {
if (response.status !== PresenceStatus.OutOfOffice) {
return false; // OOF details only valid with OOF status
}

const { startTime, endTime, message } = response.outOfOfficeDetails;
if (!startTime || !endTime || !message || typeof message !== 'string') {
return false;
}

// Validate date strings
try {
new Date(startTime).toISOString();
new Date(endTime).toISOString();
} catch {
return false;
}
}

return true;
}

public deserialize(response: UserPresence): UserPresence {
Expand Down Expand Up @@ -167,6 +228,7 @@ export function getPresence(params: GetPresenceParams): Promise<UserPresence> {
* - The library has not been initialized
* - The status parameter is invalid
* - The custom message parameter is invalid
* - The out of office details are invalid
*/
export function setPresence(params: SetPresenceParams): Promise<void> {
ensureInitialized(runtime, FrameContexts.content);
Expand All @@ -177,6 +239,7 @@ export function setPresence(params: SetPresenceParams): Promise<void> {

validateStatus(params.status);
validateCustomMessage(params.customMessage);
validateOutOfOfficeDetails(params.status, params.outOfOfficeDetails);

return callFunctionInHostAndHandleResponse(
'presence.setPresence',
Expand Down Expand Up @@ -235,3 +298,53 @@ function validateCustomMessage(customMessage: unknown): void {
throw new Error(`Error code: ${ErrorCode.INVALID_ARGUMENTS}, message: Custom message must be a string`);
}
}

/**
* Validates out of office details if provided
* @param status Current presence status
* @param details Out of office details to validate
* @throws Error if details are invalid
*/
function validateOutOfOfficeDetails(status: PresenceStatus, details?: OutOfOfficeDetails): void {
if (!details) {
if (status === PresenceStatus.OutOfOffice) {
throw new Error(
`Error code: ${ErrorCode.INVALID_ARGUMENTS}, ` +
'message: Out of office details required when status is OutOfOffice',
);
}
return;
}

if (status !== PresenceStatus.OutOfOffice) {
throw new Error(
`Error code: ${ErrorCode.INVALID_ARGUMENTS}, ` +
'message: Out of office details only valid when status is OutOfOffice',
);
}

const { startTime, endTime, message } = details;

if (!startTime || !endTime || !message) {
throw new Error(
`Error code: ${ErrorCode.INVALID_ARGUMENTS}, ` +
'message: Out of office details must include startTime, endTime, and message',
);
}

if (typeof message !== 'string') {
throw new Error(`Error code: ${ErrorCode.INVALID_ARGUMENTS}, message: Out of office message must be a string`);
}

try {
const start = new Date(startTime);
const end = new Date(endTime);
if (end <= start) {
throw new Error(
`Error code: ${ErrorCode.INVALID_ARGUMENTS}, ` + 'message: Out of office end time must be after start time',
);
}
} catch {
throw new Error(`Error code: ${ErrorCode.INVALID_ARGUMENTS}, message: Invalid date format for out of office times`);
}
}
Loading