// 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(); }