// These tests rely on the User Agent providing an implementation of
// MockWebOTPService.
//
// In Chromium-based browsers this implementation is provided by a polyfill
// in order to reduce the amount of test-only code shipped to users. To enable
// these tests the browser must be run with these options:
// // --enable-blink-features=MojoJS,MojoJSTest
import {isChromiumBased} from '/resources/test-only-api.m.js';
/**
* This enumeration is used by WebOTP WPTs to control mock backend behavior.
* See MockWebOTPService below.
*/
export const Status = {
SUCCESS: 0,
UNHANDLED_REQUEST: 1,
CANCELLED: 2,
ABORTED: 3,
};
/**
* A interface which must be implemented by browsers to support WebOTP WPTs.
*/
export class MockWebOTPService {
/**
* Accepts a function to be invoked in response to the next OTP request
* received by the mock. The (optionally async) function, when executed, must
* return an object with a `status` field holding one of the `Status` values
* defined above, and -- if successful -- an `otp` field containing a
* simulated OTP string.
*
* Tests will call this method directly to inject specific response behavior
* into the browser-specific mock implementation.
*/
async handleNextOTPRequest(responseFunc) {}
}
/**
* Returns a Promise resolving to a browser-specific MockWebOTPService subclass
* instance if one is available.
*/
async function createBrowserSpecificMockImpl() {
if (isChromiumBased) {
return await createChromiumMockImpl();
}
throw new Error('Unsupported browser.');
}
const asyncMock = createBrowserSpecificMockImpl();
export function expectOTPRequest() {
return {
async andReturn(callback) {
const mock = await asyncMock;
mock.handleNextOTPRequest(callback);
}
}
}
/**
* Instantiates a Chromium-specific subclass of MockWebOTPService.
*/
async function createChromiumMockImpl() {
const {SmsStatus, WebOTPService, WebOTPServiceReceiver} = await import(
'/gen/third_party/blink/public/mojom/sms/webotp_service.mojom.m.js');
const MockWebOTPServiceChromium = class extends MockWebOTPService {
constructor() {
super();
this.mojoReceiver_ = new WebOTPServiceReceiver(this);
this.interceptor_ =
new MojoInterfaceInterceptor(WebOTPService.$interfaceName);
this.interceptor_.oninterfacerequest = (e) => {
this.mojoReceiver_.$.bindHandle(e.handle);
};
this.interceptor_.start();
this.requestHandlers_ = [];
Object.freeze(this);
}
handleNextOTPRequest(responseFunc) {
this.requestHandlers_.push(responseFunc);
}
async receive() {
if (this.requestHandlers_.length == 0) {
throw new Error('Mock received unexpected OTP request.');
}
const responseFunc = this.requestHandlers_.shift();
const response = await responseFunc();
switch (response.status) {
case Status.SUCCESS:
if (typeof response.otp != 'string') {
throw new Error('Mock success results require an OTP string.');
}
return {status: SmsStatus.kSuccess, otp: response.otp};
case Status.UNHANDLED_REQUEST:
return {status: SmsStatus.kUnhandledRequest};
case Status.CANCELLED:
return {status: SmsStatus.kCancelled};
case Status.ABORTED:
return {status: SmsStatus.kAborted};
default:
throw new Error(
`Mock result contains unknown status: ${response.status}`);
}
}
async abort() {}
};
return new MockWebOTPServiceChromium();
}