Skip to content

Commit

Permalink
fix(ios): disable WebKit security with feature-toggle.
Browse files Browse the repository at this point in the history
  • Loading branch information
asafkorem committed Mar 28, 2024
1 parent baa9d6b commit 5a516fd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 42 deletions.
8 changes: 7 additions & 1 deletion detox/ios/Detox/Invocation/WKWebViewConfiguration+Detox.m
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,15 @@ + (void)load {
}

- (void)dtx_setPreferences:(WKPreferences *)preferences {
DTXPreferencesSetWebSecurityEnabled(preferences, NO);
if ([self shouldDisableWebSecurity]) {
DTXPreferencesSetWebSecurityEnabled(preferences, NO);
}

[self dtx_setPreferences:preferences];
}

- (BOOL)shouldDisableWebSecurity {
return [NSUserDefaults.standardUserDefaults boolForKey:@"DTXDisableWebKitSecurity"];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ extension WebCodeBuilder {
var frameElements = getAllElements(frameDoc, selector);
elements = elements.concat(frameElements);
} catch(e) {
throw e;
/* Probably issues accessing iframe documents (CORS restrictions) */
}
}
return elements;
};
var allElements = getAllElements(document, '\(baseSelector)');
Expand Down
106 changes: 66 additions & 40 deletions detox/test/e2e/29.webview.test.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const {expectElementSnapshotToMatch} = require("./utils/snapshot");
const {waitForCondition} = require("./utils/waitForCondition");
const {expectToThrow} = require('./utils/custom-expects');

const jestExpect = require('expect').default;

const MockServer = require('../mock-server/mock-server');

describe('Web View', () => {
describe('WebView', () => {
beforeEach(async () => {
await device.reloadReactNative();
await element(by.text('WebView')).tap();
Expand All @@ -31,15 +33,15 @@ describe('Web View', () => {
});

it('should raise an error when element does not exists but expect to exist', async () => {
await jestExpect(async () => {
await expectToThrow(async () => {
await expect(web.element(by.web.id('nonExistentElement'))).toExist();
}).rejects.toThrowError();
});
});

it('should raise an error when does element not exists at index', async () => {
await jestExpect(async () => {
await expectToThrow(async () => {
await expect(web.element(by.web.tag('p')).atIndex(100)).toExist();
}).rejects.toThrowError();
});
});
});

Expand Down Expand Up @@ -258,9 +260,9 @@ describe('Web View', () => {
it('should raise error when script fails', async () => {
const headline = web.element(by.web.id('pageHeadline'));

await jestExpect(async () => {
await expectToThrow(async () => {
await headline.runScript('(el) => { el.textContent = "Changed"; throw new Error("Error"); }');
}).rejects.toThrowError();
});
});
});

Expand All @@ -287,35 +289,6 @@ describe('Web View', () => {
});
});

describe(':ios: inner frame', () => {
/** @type {Detox.WebViewElement} */
let webview;
const mockServer = new MockServer();

beforeAll(async () => {
mockServer.init();

if (device.getPlatform() === 'android') {
// Android needs to reverse the port in order to access the mock server
await device.reverseTcpPort(mockServer.port);
}
});

afterAll(async () => {
await mockServer.close();
});

beforeEach(async () => {
await element(by.id('toggle3rdWebviewButton')).tap();
webview = web(by.id('webView'));
});

it('should find element in inner frame', async () => {
await expect(webview.element(by.web.tag('h1'))).toExist();
await expect(webview.element(by.web.tag('h1'))).toHaveText('Hello World!');
});
});

describe('multiple web-views scenario',() => {
/** @type {Detox.WebViewElement} */
let webview;
Expand Down Expand Up @@ -358,18 +331,71 @@ describe('Web View', () => {
});

it('should throw on index out of bounds', async () => {
await jestExpect(async () => {
await expectToThrow(async () => {
await expect(web(by.id('webView')).atIndex(2).element(by.web.id('message'))).toExist();
}).rejects.toThrowError();
});
});
});

// Not implemented yet
it(':android: should throw on usage of atIndex', async () => {
await jestExpect(async () => {
await expectToThrow(async () => {
await expect(web(by.id('webView')).atIndex(0).element(by.web.id('message'))).toExist();
}).rejects.toThrowError();
});
});
});
});
});

describe(':ios: WebView CORS (inner frame)', () => {
/** @type {Detox.WebViewElement} */
let webview;
const mockServer = new MockServer();

beforeAll(async () => {
mockServer.init();

if (device.getPlatform() === 'android') {
// Android needs to reverse the port in order to access the mock server
await device.reverseTcpPort(mockServer.port);
}
});

afterAll(async () => {
await mockServer.close();
});

const launchAndNavigateToInnerFrame = async (shouldDisableWebKitSecurity) => {
await device.launchApp({
newInstance: true,
launchArgs: {
DTXDisableWebKitSecurity:
shouldDisableWebKitSecurity !== undefined ? (shouldDisableWebKitSecurity ? 'true' : 'false') : undefined,
},
});

await element(by.text('WebView')).tap();
await element(by.id('toggle3rdWebviewButton')).tap();

webview = web(by.id('webView'));
};

it('should find element in cross-origin frame when `DTXDisableWebKitSecurity` is `true`', async () => {
await launchAndNavigateToInnerFrame(true);

await expect(webview.element(by.web.tag('h1'))).toExist();
await expect(webview.element(by.web.tag('h1'))).toHaveText('Hello World!');
});

it('should not find element in cross-origin frame when `DTXDisableWebKitSecurity` is `false`', async () => {
await launchAndNavigateToInnerFrame(false);

await expect(webview.element(by.web.tag('h1'))).not.toExist();
});

it('should not find element in cross-origin frame when `DTXDisableWebKitSecurity` is not set', async () => {
await launchAndNavigateToInnerFrame();

await expect(webview.element(by.web.tag('h1'))).not.toExist();
});
});

0 comments on commit 5a516fd

Please sign in to comment.