Skip to content

Add Puppeteer #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/before_deploy.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env bash

set -e

curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl
Expand Down
2 changes: 2 additions & 0 deletions ci/before_install.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#!/usr/bin/env bash

set -e

if [[ $TRAVIS_PULL_REQUEST != 'false' ]]; then echo 'Skipping deployment for pull requests';
else
if [[ -z "${PROJECT_ID}" ]]; then echo 'PROJECT_ID is not defined in your travis environement variables.'; fi
Expand Down
1 change: 1 addition & 0 deletions ci/deploy.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env bash

set -e

# Update dependencies and docker image end push them taking care to separate by repositories and branches.
echo 'deploy script'
Expand Down
1 change: 1 addition & 0 deletions ci/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ docker-compose exec php bin/behat --format=progress
docker-compose exec client yarn install --pure-lockfile
docker-compose exec client yarn jest --ci --reporters=default --reporters=jest-junit --passWithNoTests
docker-compose exec client yarn eslint src
docker-compose exec client yarn e2e
curl -s http://localhost # Client
curl -s http://localhost:81 # Admin
curl -s http://localhost:8080 # API
Expand Down
2 changes: 2 additions & 0 deletions client/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@
**/.dockerignore
Dockerfile*
.env*
*.crt
*.key
4 changes: 4 additions & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/features/screenshots/*
!/features/screenshots/.gitignore
*.crt
*.key
28 changes: 27 additions & 1 deletion client/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,40 @@ FROM node:11.5-alpine

RUN mkdir -p /usr/src/client

ARG PUPPETEER_VERSION=1.10.0

WORKDIR /usr/src/client

RUN yarn global add @api-platform/client-generator
RUN apk add --no-cache ca-certificates chromium; \
apk add --no-cache --virtual .gyp python bash make g++; \
apk add --no-cache --virtual .build-deps openssl; \
yarn global add @api-platform/client-generator puppeteer@${PUPPETEER_VERSION}; \
yarn cache clean; \
apk del .gyp

ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
ENV NODE_PATH="/usr/local/share/.config/yarn/global/node_modules:${NODE_PATH}"
ENV CI=true

RUN openssl genrsa -des3 -passout pass:NotSecure -out cert.pass.key 2048; \
openssl rsa -passin pass:NotSecure -in cert.pass.key -out cert.key; \
openssl req -new -passout pass:NotSecure -key cert.key -out cert.csr -subj '/C=SS/ST=SS/L=Gotham City/O=API Platform Dev/CN=localhost'; \
openssl x509 -req -sha256 -days 365 -in cert.csr -signkey cert.key -out cert.crt; \
mv cert.key /usr/local/share/cert.key; \
mv cert.crt /usr/local/share/cert.crt; \
apk del .build-deps

# Prevent the reinstallation of node modules at every changes in the source code
COPY package.json yarn.lock ./
RUN yarn install

COPY docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint
ENTRYPOINT ["docker-entrypoint"]

COPY . ./

EXPOSE 3000
EXPOSE 5000

CMD yarn start
11 changes: 11 additions & 0 deletions client/docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh
set -e

if [ ! -f cert.key ]; then
cp /usr/local/share/cert.key ./
fi
if [ ! -f cert.crt ]; then
cp /usr/local/share/cert.crt ./
fi

exec "$@"
10 changes: 10 additions & 0 deletions client/features/features/books.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This is a demo feature applied to the demo books list component.
# Please remove them and create yours.
Feature:
As any user,
when I go to books list,
I see a list of books.

Scenario: I see a list of books
When I go to the books list
Then I see a list of books
41 changes: 41 additions & 0 deletions client/features/hooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
const { After, AfterAll } = require('cucumber');
const { scope } = require('./support');
const path = require('path');

After(async scenario => {
const page = scope.context.currentPage;

if (!page) {
return;
}

if ('failed' === scenario.result.status) {
await page.screenshot({
path: path.join(
__dirname,
'screenshots',
`${scenario.pickle.name
.replace(/[^A-z\d]+/gi, '-')
.replace(/[-]{2,}/gi, '-')}-${Date.now().toString()}.png`
)
});
}

if (scope.browser) {
const cookies = await page.cookies();
if (cookies && cookies.length > 0) {
await page.deleteCookie(...cookies);
}
await page.evaluate(() => localStorage.clear());
await page.close();

scope.context.currentPage = null;
}
});

AfterAll(async () => {
if (scope.browser) {
await scope.browser.close();
}
scope.server.shutdown();
});
6 changes: 6 additions & 0 deletions client/features/pages/book.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
url: '/books/',
selectors: {
title: 'h1',
},
};
3 changes: 3 additions & 0 deletions client/features/pages/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
book: require('./book'),
};
Empty file.
30 changes: 30 additions & 0 deletions client/features/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const express = require('express');
const httpShutdown = require('http-shutdown');
const path = require('path');
const https = require('https');
const fs = require('fs');

const app = express();

const serverPort = 5000;

const assetsPath = path.join(__dirname, '../build');
const indexPath = path.join(assetsPath, 'index.html');

app.use(express.static(assetsPath));

app.use((req, res) => {
res.sendFile(indexPath);
});

const server = httpShutdown(
https.createServer({
rejectUnauthorized: false,
key: fs.readFileSync('cert.key'),
cert: fs.readFileSync('cert.crt'),
}, app).listen(serverPort)
);

server.host = `https://localhost:${serverPort}`;

module.exports = server;
15 changes: 15 additions & 0 deletions client/features/step_definitions/books.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const { Then, When } = require('cucumber');
const { book } = require('../pages');
const { assert, navigation, scope } = require('../support');

When('I go to the books list', async () => {
await navigation.goto(book.url);
});

Then('I see a list of books', async () => {
await scope.context.currentPage.waitForSelector(book.selectors.title, {
visible: true,
timeout: navigation.SELECTOR_TIMEOUT,
});
await assert.containsText(book.selectors.title, 'Book List');
});
26 changes: 26 additions & 0 deletions client/features/support/assert.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const assert = require('assert');
const scope = require('./scope');

const containsText = async (selector, value) => {
const label = await scope.context.currentPage.$eval(selector, el => el.innerText);

return assert.equal(label.toString().trim(), value.toString().trim());
};

const inputValueEquals = async (selector, expectedValue) => {
const actualValue = await scope.context.currentPage.$eval(selector, el => el.value);

return assert.equal(actualValue, expectedValue);
};

const urlEquals = async (url) => {
const pageUrl = await scope.context.currentPage.url();

return assert.equal(pageUrl, scope.host + url);
};

module.exports = {
containsText,
urlEquals,
inputValueEquals,
};
46 changes: 46 additions & 0 deletions client/features/support/form.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const scope = require('./scope');

const blurField = async (selector) => {
await scope.context.currentPage.$eval(selector, e => e.blur());
};

const clearField = async (selector) => {
await scope.context.currentPage.evaluate(selector => {
const item = document.querySelector(selector);
item.focus();
item.value = '';
document.querySelector(selector).blur();
}, selector);
};

const fillField = async (selector, value) => {
const page = scope.context.currentPage;
await page.focus(selector);
await page.keyboard.type(value);
};

const clearAndFillField = async (selector, value) => {
await clearField(selector);
await fillField(selector, value);
};

const submit = async (selector, waitForNavigation = true) => {
const page = scope.context.currentPage;
await page.$eval(selector, form => form.submit());
if (waitForNavigation) {
await page.waitForNavigation();
}
};

const click = async (selector) => {
await scope.context.currentPage.click(selector);
};

module.exports = {
blurField,
clearAndFillField,
clearField,
click,
fillField,
submit,
};
6 changes: 6 additions & 0 deletions client/features/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
assert: require('./assert'),
form: require('./form'),
navigation: require('./navigation'),
scope: require('./scope'),
};
33 changes: 33 additions & 0 deletions client/features/support/navigation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const scope = require('./scope');

const SELECTOR_TIMEOUT = 5000;

const goto = async (page = '') => {
if (!scope.browser) {
const isCIEnv = !!process.env.CI;
scope.browser = await scope.driver.launch({
ignoreHTTPSErrors: true,
headless: isCIEnv,
slowMo: isCIEnv ? 0 : 2,
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
}

if (!scope.context.currentPage) {
scope.context.currentPage = await scope.browser.newPage();
scope.context.currentPage.setViewport({
width: 1600,
height: 1024,
});
}

await scope.context.currentPage.goto(scope.host + page, {
timeout: SELECTOR_TIMEOUT,
waitUntil: 'networkidle2',
});
};

module.exports = {
goto,
SELECTOR_TIMEOUT,
};
1 change: 1 addition & 0 deletions client/features/support/scope.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {};
13 changes: 13 additions & 0 deletions client/features/world.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { setWorldConstructor } = require('cucumber');
const puppeteer = require('puppeteer');
const { scope } = require('./support');
const server = require('./server');

const World = function () {
scope.driver = puppeteer;
scope.context = {};
scope.host = server.host;
scope.server = server;
};

setWorldConstructor(World);
13 changes: 12 additions & 1 deletion client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"pree2e": "react-scripts build",
"e2e": "cucumber-js",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
Expand All @@ -42,5 +44,14 @@
"not dead",
"not ie <= 11",
"not op_mini all"
]
],
"devDependencies": {
"assert": "^1.4.1",
"cucumber": "^5.1.0",
"express": "^4.16.4",
"fs": "^0.0.1-security",
"http-shutdown": "^1.2.0",
"https": "^1.0.0",
"path": "^0.12.7"
}
}
Loading