Вход в Google с Playwright в среде CI
У меня есть сценарий драматурга / мокко, который позволяет мне войти в Google. Этот код работает при запуске на моем локальном компьютере, но не работает при запуске вgitlab-ci
.
e2e.spec.js
describe('Staging', async function() {
before(async function() {
await playwright.selectors.register(selectorEngine, { name: 'shadow' });
browser = await playwright.chromium.launch({
args: [
'--disable-web-security',
'--disable-features=IsolateOrigins,site-per-process',
],
});
context = await browser.newContext({
geolocation: { latitude: 40.0583, longitude: 74.4057 },
viewport: playwright.devices['Pixel 2'].viewport,
});
page = await context.newPage();
await page.addScriptTag({ content: `./headless-spoof.js` });
});
afterEach(async function() {
await page.screenshot({ path: `screenshots/${screenshotcounter++} - ${this.currentTest.title.replace(/\s+/g, '_')}.png` });
});
after(async function() {
await page.close();
await context.close();
await browser.close();
});
describe('Signing In, Joining, and Cancelling', function() {
before(goto('/subscribe?plan=gold'));
it('Logs in with Google', async function() {
// Click the "Sign in with Google" Button
const selector = 'shadow=[data-provider-id="google.com"]';
await page.waitForSelector(selector, { visibility: 'visible' });
const login = await page.$(selector);
await login.click();
await page.waitForNavigation();
await new Promise(r => setTimeout(r, 5 * 1000));
// Disambiguate: Which version of the Google sign-in form is it?
const content = await page.evaluate(body => body.innerHTML, await page.$('body'));
const isMobileLoginPage = content.includes('One account. All of Google.');
// Fill Username
const userNameSelector = isMobileLoginPage ? '#Email' : '#identifierId';
await page.waitForSelector(userNameSelector, { visibility: 'visible' });
const userNameField = await page.$(userNameSelector);
await userNameField.fill(process.env.GMAIL_USER);
await userNameField.press('Enter');
// Fill Password
const passwordSelector = isMobileLoginPage ? '#Passwd' : 'input[type="password"]';
await page.waitForSelector(passwordSelector, { visibility: 'visible' });
const passwordField = await page.$(passwordSelector);
await passwordField.fill(process.env.GMAIL_PASS);
await passwordField.press('Enter');
await page.waitForNavigation();
// Meanwhile, back at the ranch...
const subscribeSelector = 'shadow=#payment h2';
await page.waitForSelector(subscribeSelector, { visibility: 'visible' });
const heading = await page.$eval(subscribeSelector, textContent);
expect(heading).to.match(/Secure Payment/);
});
});
});
headless-spoof.js
// overwrite the `languages` property to use a custom getter
Object.defineProperty(window.navigator, 'languages', {
get: function() {
return ['en-US', 'en'];
},
});
// overwrite the `plugins` property to use a custom getter
Object.defineProperty(window.navigator, 'plugins', {
get: function() {
// this just needs to have `length > 0`, but we could mock the plugins too
return [1, 2, 3, 4, 5];
},
});
const { getParameter } = WebGLRenderingContext;
WebGLRenderingContext.prototype.getParameter = function(parameter) {
// UNMASKED_VENDOR_WEBGL
if (parameter === 37445)
return 'Intel Open Source Technology Center';
// UNMASKED_RENDERER_WEBGL
if (parameter === 37446)
return 'Mesa DRI Intel(R) Ivybridge Mobile ';
return getParameter(parameter);
};
['height', 'width'].forEach(property => {
// store the existing descriptor
const imageDescriptor = Object.getOwnPropertyDescriptor(HTMLImageElement.prototype, property);
// redefine the property with a patched descriptor
Object.defineProperty(HTMLImageElement.prototype, property, {
...imageDescriptor,
get: function() {
// return an arbitrary non-zero dimension if the image failed to load
if (this.complete && this.naturalHeight == 0)
return 20;
// otherwise, return the actual dimension
return imageDescriptor.get.apply(this);
},
});
});
// store the existing descriptor
const elementDescriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'offsetHeight');
// redefine the property with a patched descriptor
Object.defineProperty(HTMLDivElement.prototype, 'offsetHeight', {
...elementDescriptor,
get: function() {
if (this.id === 'modernizr')
return 1;
return elementDescriptor.get.apply(this);
},
});
e2e:chrome:
image: arjun27/playwright-bionic:0.2.0
environment: staging
stage: e2e
only:
- development
script:
- mkdir -p screenshots
- npx mocha -r dotenv/config 'test/*.spec.js' --bail --timeout 15000
artifacts:
paths:
- screenshots
expire_in: 1 week
when: always
Несмотря на эти меры предосторожности, я все еще вижу этот результат в артефактах моей работы CI:
Могу ли я что-нибудь сделать в своих тестовых сценариях или с рассматриваемой учетной записью Google, чтобы преодолеть это препятствие?