Skip to content

Commit

Permalink
Add custom child iframe in teams test app for nested app auth e2e tes…
Browse files Browse the repository at this point in the history
…ts (#2649)

* Add custom child iframe in teams test app for E2E tests

* Update as per reviewer's feedback
  • Loading branch information
baljesingh authored Dec 5, 2024
1 parent a51cd2b commit 57901e9
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 17 deletions.
51 changes: 34 additions & 17 deletions apps/teams-test-app/src/components/NestedAppAuthAPIs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -152,36 +152,58 @@ const NestedAppAuthAPIs = (): ReactElement => {
}),
});

const AddChildIframeSection = (): React.ReactElement | null => {
const AddChildIframeSection = (): React.ReactElement => {
const [iframeAdded, setIframeAdded] = useState(false);

const addChildIframe = (): void => {
if (iframeAdded) {
console.log('Iframe already added.');
return;
}

const iframeContainer = document.getElementById('nestedChildIframeContainer');
if (!iframeContainer) {
console.error('Container not found: nestedChildIframeContainer');
console.error('Iframe container not found');
return;
}

const childIframe = document.createElement('iframe');
childIframe.src = `${window.location.href}?appInitializationTest=true&groupedMode=NestedAppAuthAPIs`;
childIframe.src = '/naa_childIframe.html';
childIframe.id = 'nestedAuthChildIframe';
childIframe.width = '100%';
childIframe.height = '400px';
childIframe.style.width = '100%';
childIframe.style.height = '400px';
childIframe.style.border = 'none';

iframeContainer.appendChild(childIframe);
setIframeAdded(true);

// Send payload to the iframe after it loads
childIframe.onload = () => {
const payloads = {
defaultPayloadForBridge: NestedAppAuthRequest,
defaultPayloadForTopWindow: JSON.stringify({
id: '2',
func: 'nestedAppAuth.execute',
args: [],
data: NestedAppAuthRequest,
}),
};
childIframe.contentWindow?.postMessage(payloads, window.location.origin);
};
};

return (
<div style={{ border: '5px solid black', padding: '2px', margin: '2px' }}>
<h2>Add Nested Child Iframe</h2>
<div
className="boxAndButton"
id="box_naaNestedChildIframe"
style={{
border: '5px solid black',
padding: '5px',
margin: '1px',
}}
>
<h2>Child Iframe</h2>
<input
id="button_addNestedChildIframe"
name="button_addNestedChildIframe"
type="button"
value="Add Child Iframe"
Expand All @@ -191,19 +213,14 @@ const NestedAppAuthAPIs = (): ReactElement => {
<div
id="nestedChildIframeContainer"
style={{
marginTop: '2px',
height: '400px',
border: '2px solid red',
overflow: 'hidden',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
marginTop: '20px',
height: '450px',
border: '1px solid red',
}}
></div>
/>
</div>
);
};

return (
<ModuleWrapper title="NestedAppAuth">
<CheckIsNAAChannelRecommended />
Expand Down
230 changes: 230 additions & 0 deletions apps/teams-test-app/src/public/naa_childIframe.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nested Child Iframe Content</title>
<style>
.input-container input[type='text'] {
margin-top: 10px;
padding: 5px;
width: 180px;
}

.input-container input[type='button'] {
width: 100px;
}

.input-row {
display: flex;
gap: 5px;
margin-bottom: 10px;
}

.response-box {
display: flex;
margin-top: 10px;
border: 1px solid #ccc;
padding: 2px;
height: 30px;
}

.default-button {
margin-left: 0;
}

.section-container h4 {
margin-top: 5px;
font-size: 16px;
}

.section-container {
border: 1px solid #ccc;
padding: 15px;
margin-bottom: 20px;
border-radius: 5px;
}
</style>
</head>
<body>
<div>
<div class="section-container">
<h4>NestedAppAuthBridge</h4>
<div class="input-row">
<input
id="input_childPayloadForBridge"
name="input_childPayloadForBridge"
type="text"
placeholder="Payload for NestedAppAuthBridge"
/>
<input
id="button_sendPayloadToBridge"
name="button_sendPayloadToBridge"
type="button"
value="Send to NAA Bridge"
onClick="sendMessageToBridge()"
/>
</div>
<input
id="button_defaultPayloadForBridge"
name="button_defaultPayloadForBridge"
type="button"
value="Default"
class="default-button"
onClick="setDefaultPayloadForBridge()"
/>
<div id="text_bridge_response_for_child" class="response-box">No response yet</div>
</div>

<div class="section-container">
<h4>Top Window</h4>
<div class="input-row">
<input
id="input_childPayloadForTopWindow"
name="input_childPayloadForTopWindow"
type="text"
placeholder="Payload for Top Window"
/>
<input
id="button_sendPayloadToTopWindow"
name="button_sendPayloadToTopWindow"
type="button"
value="Send to Top Window"
onClick="sendMessageToTopWindow()"
/>
</div>
<input
id="button_defaultPayloadForTopWindow"
name="button_defaultPayloadForTopWindow"
type="button"
value="Default"
class="default-button"
onClick="setDefaultPayloadForTopWindow()"
/>
<div id="text_topWindow_response_for_child" class="response-box">No response yet</div>
</div>
</div>

<script>
let defaultPayloadForBridge = '';
let defaultPayloadForTopWindow = '';

// Listen for messages from the parent
window.addEventListener('message', (event) => {
// Validate the origin if known
if (event.origin !== window.location.origin) return;

const data = event.data;
if (data.defaultPayloadForBridge) {
defaultPayloadForBridge = data.defaultPayloadForBridge;
}
if (data.defaultPayloadForTopWindow) {
defaultPayloadForTopWindow = data.defaultPayloadForTopWindow;
}
});

const targetOrigin = 'https://local.teams.office.com:8080';

// Utility function to display a response in a given element
const displayResponse = (element, message) => {
element.innerText = message;
};

// Functions to set default payloads
function setDefaultPayloadForBridge() {
const inputElement = document.getElementById('input_childPayloadForBridge');
if (inputElement) {
inputElement.value = defaultPayloadForBridge;
}
}

function setDefaultPayloadForTopWindow() {
const inputElement = document.getElementById('input_childPayloadForTopWindow');
if (inputElement) {
inputElement.value = defaultPayloadForTopWindow;
}
}

// Send Message to NestedAppAuthBridge
function sendMessageToBridge() {
const inputElement = document.getElementById('input_childPayloadForBridge');
const responseElement = document.getElementById('text_bridge_response_for_child');

if (!inputElement || !responseElement) return;

try {
// Validate and send the payload
const payload = JSON.parse(inputElement.value);

// Inline validation for NestedAppAuthRequest
if (!payload) throw new Error('Input is required.');
if (payload.messageType !== 'NestedAppAuthRequest') {
throw new Error('Invalid or missing messageType. Expected "NestedAppAuthRequest".');
}
if (!payload.method) throw new Error('Method name is required in payload.');
if (!payload.requestId) throw new Error('RequestId is required in payload.');

const bridge = window.nestedAppAuthBridge;
if (!bridge) {
displayResponse(responseElement, 'NAA Bridge not available');
return;
}

bridge.addEventListener('message', (response) => {
displayResponse(responseElement, JSON.stringify(response));
});

bridge.postMessage(JSON.stringify(payload));
displayResponse(responseElement, 'Message sent to Bridge. Awaiting response...');
} catch (error) {
console.error('Validation error:', error.message);
displayResponse(responseElement, `Validation Error: ${error.message}`);
}
}

// Send Message to Top Window
function sendMessageToTopWindow() {
const inputElement = document.getElementById('input_childPayloadForTopWindow');
const responseElement = document.getElementById('text_topWindow_response_for_child');

if (!inputElement || !responseElement) return;

try {
// Validate and send the payload
const payload = JSON.parse(inputElement.value);

// Inline validation for Top Window
if (!payload) throw new Error('Input is required.');
if (!payload.id) throw new Error('"id" is required.');
if (!payload.func) throw new Error('"func" is required.');
if (!payload.data) throw new Error('"data" is required with NAA payload.');

// Validate nested NAA payload in "data"
const nestedData = JSON.parse(payload.data);
if (nestedData.messageType !== 'NestedAppAuthRequest') {
throw new Error('Invalid or missing messageType in NAA payload. Expected "NestedAppAuthRequest".');
}

if (!window.top) {
displayResponse(responseElement, 'Top window not accessible');
return;
}

window.addEventListener('message', (event) => {
if (event.origin !== targetOrigin) {
console.warn('Received message from unexpected origin:', event.origin);
return;
}
displayResponse(responseElement, JSON.stringify(event.data));
});

window.top.postMessage(payload, targetOrigin);
displayResponse(responseElement, 'Message sent to Top Window. Awaiting response...');
} catch (error) {
console.error('Validation error:', error.message);
displayResponse(responseElement, `Validation Error: ${error.message}`);
}
}
</script>
</body>
</html>

0 comments on commit 57901e9

Please sign in to comment.