Merge branch 'master' into release/rework-css

This commit is contained in:
Jean-Baptiste Pasquier 2020-05-19 15:29:17 +02:00
commit 1d6015bc04
No known key found for this signature in database
GPG Key ID: F2702E6D22ED4D62
19 changed files with 1614 additions and 7 deletions

7
.gitignore vendored
View File

@ -1,6 +1,11 @@
**/node_modules **/node_modules
**/config.json config.json
.DS_Store
*.iml *.iml
*.swp *.swp
dist dist
cypress/screenshots
cypress/videos
cache
.npm
.DS_Store .DS_Store

View File

@ -1,13 +1,56 @@
image: node:11 # workflow
stages: stages:
- build
- test
- integration - integration
- acceptance - acceptance
- release - release
- deployment - deployment
# default image for jobs
default:
image: node:11
# cache modules between jobs
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
## BUILD ##
build:
stage: build
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- cp config.sample.json config.json
- npm run build
artifacts:
when: on_success
expire_in: 1 day
paths:
- dist/
tags:
- test
## TESTING ## ## TESTING ##
test:e2e:
stage: test
image: cypress/included:4.5.0
services:
- name: ${CI_REGISTRY_IMAGE}/server:0.1
before_script:
# install missing dependencies
- npm install -g sirv-cli
# making sure the process is orphan
- sirv dist --port 3000 > /dev/null 2>&1 &
script:
- cypress run -e CYPRESS_baseUrl=http://localhost:3000
tags:
- test
## VALIDATION ## ## VALIDATION ##
test1: test1:

View File

@ -221,6 +221,34 @@ On `config.json`:
} }
``` ```
## Use with docker
### Multi services
Run with a local binding on localhost:
```bash
docker-compose build
docker-compose up -d client server
```
Use in CI context:
```bash
docker-compose -f docker-compose.yml build
docker-compose -f docker-compose.yml up -d client server
docker-compose -f docker-compose.yml run --rm e2e
```
Build and push the server to registry:
```bash
docker build -f docker/djangoldp.docker --build-arg serve="http://localhost:8000" -t registry.startinblox.com/applications/hubl/server:0.1 .
docker push registry.startinblox.com/applications/hubl/server:0.1
```
Note: within a Kubernetes pod all services are bound to `localhost`.
## Built With ## Built With
* [Sib-Core](https://git.startinblox.com/framework/sib-core/) - An awesome new framework! * [Sib-Core](https://git.startinblox.com/framework/sib-core/) - An awesome new framework!

View File

@ -3,8 +3,8 @@
"authority": "http://localhost:8000/", "authority": "http://localhost:8000/",
"authorityName": "djangoldp-server-name", "authorityName": "djangoldp-server-name",
"endpoints": { "endpoints": {
"groups": "http://localhost:8000/groups/",
"skills": "http://localhost:8000/skills/", "skills": "http://localhost:8000/skills/",
"users": "http://localhost:8000/users/", "users": "http://localhost:8000/users/"
"groups": "http://localhost:8000/groups/"
} }
} }

7
cypress.json Normal file
View File

@ -0,0 +1,7 @@
{
"baseUrl": "http://127.0.0.1:3000",
"defaultCommandTimeout": 60000,
"chromeWebSecurity": false,
"viewportWidth": 1920,
"viewportHeight": 1080
}

View File

@ -0,0 +1,5 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -0,0 +1,108 @@
/// <reference types="Cypress" />
context('Browser testing', () => {
before(() => {
cy.clearLocalStorage({ domain: null});
cy.clearCookies({ domain: null });
});
it('visit the homepage', () => {
cy.visit('/');
});
it('should await for an user login', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/auth/login/');
});
});
describe('Login process', () => {
it('should write "admin" on username field and "password" on password field', () => {
cy.get('#id_username').type('admin');
cy.get('#id_username').should('have.value', 'admin');
cy.get('#id_password').type('password');
cy.get('#id_password').should('have.value', 'password');
});
it('should click on login button', () => {
cy.get(':nth-child(1) > .flex-column > [type="submit"]').click();
});
it('should provide an error, username and password mismatch.', () => {
cy.get('.error').should('contain.text', 'Ton nom d\'utilisateur et ton mot de passe ne correspondent pas. Réessaye.')
});
it('should write "admin" on password field then press the login button', () => {
cy.get('#id_password').type('admin');
cy.get('#id_password').should('have.value', 'admin');
cy.get(':nth-child(1) > .flex-column > [type="submit"]').click();
});
it('should ask for user permission to access their datas.', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/authorize')
});
cy.get('.accept-button').click();
});
// it('should redirect the user to the app.', () => {
// cy.get('.accept-button').click();
// cy.location().should((loc) => {
// expect(loc.protocol + "//" + loc.host).to.eq(Cypress.config().baseUrl);
// expect(loc.pathname).to.eq('/');
// });
// });
// });
// describe('Main interface', () => {
// // it('should show my name on top right', () => {
// // cy.get('sib-display-value').contains('Admin');
// // });
// it('should show my username on the left menu', () => {
// cy.get('[fields="username, badge"][data-src="http://localhost:8000/users/admin/"] > :nth-child(1) > sib-display-div > div').contains('admin');
// });
// it('should open a chat with myself', () => {
// cy.get('[fields="username, badge"][data-src="http://localhost:8000/users/admin/"] > :nth-child(1) > sib-display-div > div').click();
// cy.get('.name').contains('admin');
// });
// it('should not work, because I have no Prosody configured', () => {
// cy.get('.content-box > .chat-view > sib-chat').should('be.empty');
// });
// describe('Circles', () => {
// it('should navigate the Administration from left menu', () => {
// cy.get('.create > sib-link').click();
// cy.get('#admin-circles > .content-box > .content-box__header > .without-margin').contains('Administration');
// cy.get('#admin-circle-list > .content-box__info > .admin-header > .admin-header__title').contains('Circles');
// });
// it('should navigate to Circle creation', () => {
// cy.get('#admin-circle-list > .content-box__info > .admin-header > .button').click();
// cy.location().should((loc) => {
// expect(loc.pathname).to.contain('admin-circle-create');
// });
// });
// var name = new Uint32Array(1);
// crypto.getRandomValues(name);
// name = "Test Circle " + name;
// it('should allow Circle Creation', () => {
// cy.get('form > sib-form-label-text[name="name"] > label > input').type(name);
// cy.get('.content-box__info > sib-form > form > sib-form-label-text[name="description"] > label > input').type('With a great description!');
// cy.get('#admin-circle-create > .content-box__info > sib-form > form > [type="submit"]').click();
// cy.get('#admin-circle-list > div > div.table > sib-display').contains(name);
// });
// it('should open the circle information page', () => {
// cy.screenshot();
// cy.get('#navbar-router').contains(name).click();
// cy.get('[name="circle-information"] > li').click();
// });
// it('should delete the circle', () => {
// cy.get('.box-button > sib-ac-checker > .button').click();
// cy.get('#navbar-router').should('not.contain', name);
// });
// });
// });
// describe('Logout process', () => {
// it('should properly log out the user', () => {
// cy.get('#user-controls__profile > div').click();
// cy.get('#user-controls__panel > nav > button').click();
// // Dirty fix for logout, targeting logout button is not enough.
// cy.clearLocalStorage({ domain: null });
// cy.clearCookies({ domain: null });
// cy.location().should((loc) => {
// expect(loc.pathname).to.eq('/auth/login/');
// });
// });
});
});

20
cypress/plugins/index.js Normal file
View File

@ -0,0 +1,20 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// require('cypress-terminal-report').installPlugin(on);
};

View File

@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })

30
cypress/support/index.js Normal file
View File

@ -0,0 +1,30 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
//require('cypress-terminal-report').installSupport();
// Alternatively you can use CommonJS syntax:
// require('./commands')
Cypress.on('uncaught:exception', (err, runnable) => {
// returning false here prevents Cypress from
// failing the test
console.log('Cypress detected uncaught exception', err);
return false;
});
Cypress.on('fail', () => Cypress.runner.abort())

18
docker/config.json Normal file
View File

@ -0,0 +1,18 @@
{
"xmpp": "https://jabber.happy-dev.fr/http-bind/",
"authority": "$SERVER",
"authorityName": "djangoldp-server-name",
"endpoints": {
"get": {
"circles": "$SERVER/circles/",
"groups": "$SERVER/groups/",
"users": "$SERVER/users/"
},
"post": {
"circles": "$SERVER/circles/",
"groups": "$SERVER/groups/",
"users": "$SERVER/users/"
}
},
"analytics": []
}

26
docker/djangoldp.docker Normal file
View File

@ -0,0 +1,26 @@
FROM python:3.6
LABEL maintainer="Plup <plup@plup.io>"
# get server address
ARG serve
# fix bug in manager install
ENV PATH="/root/.local/bin:${PATH}"
# install a LDP server with required functions
WORKDIR /app
RUN pip install git+https://git.startinblox.com/djangoldp-packages/server-manager.git
RUN sib startproject sibserver
COPY docker/packages.yml /app/sibserver/
RUN sed -i 's#SERVER#'"${serve:-http://localhost:8000}"'#' /app/sibserver/packages.yml
RUN cd /app/sibserver && sib install server
# fix invalid HTTP_HOST
RUN sed -i 's/ALLOWED_HOSTS\s=\s\[\]/ALLOWED_HOSTS=\["\*"\]/' /app/sibserver/server/settings.py
# run the dev server
EXPOSE 8000
ENTRYPOINT ["python", "/app/sibserver/manage.py"]
CMD ["runserver", "0.0.0.0:8000"]
# vim: ft=dockerfile:

View File

@ -0,0 +1,15 @@
---
version: '3'
services:
front:
environment:
- SERVER=http://127.0.0.1
ports:
- 80:80
server:
build:
args:
serve: http://127.0.0.1

39
docker/docker-compose.yml Normal file
View File

@ -0,0 +1,39 @@
---
version: '3'
services:
front:
build:
context: ../
dockerfile: docker/front.docker
environment:
- SERVER=http://server.org:8000
networks:
- web
server:
build:
context: ../
dockerfile: docker/djangoldp.docker
args:
serve: http://server.org:8000
networks:
web:
aliases:
- server.org
db:
e2e:
image: cypress/included:4.3.0
environment:
- CYPRESS_baseUrl=https://front
volumes:
- ../cypress:/cypress
- ../cypress.json:/cypress.json
networks:
- web
networks:
web:
db:

37
docker/front.docker Normal file
View File

@ -0,0 +1,37 @@
FROM node:12 AS builder
LABEL maintainer="Plup <plup@plup.io>"
# install dependencies
WORKDIR /build
ADD package.json package-lock.json ./
ADD src/ src/
RUN npm install --only=production
# build the app
ADD docker/config.json .
RUN npm run build
# change index to template
RUN mv /build/dist/index.html /build/dist/index.tpl
# generate a self signed certificate
RUN openssl req -x509 -newkey rsa:4096 -keyout /tmp/key.pem -out /tmp/cert.pem -days 365 -nodes -subj '/CN=localhost'
# serve
FROM nginx:latest
COPY --from=builder /build/dist /var/www
COPY --from=builder /tmp/*.pem /etc/nginx/
WORKDIR /var/www
# get specific config
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 443
# get server address
ENV SERVER http://localhost:8000
# replace addresses at runtime
CMD /bin/bash -c "envsubst '\$SERVER' < index.tpl > index.html && \
exec nginx -g 'daemon off;'"
# vim: ft=dockerfile:

9
docker/nginx.conf Normal file
View File

@ -0,0 +1,9 @@
server {
listen 443 ssl default_server;
root /var/www/;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/key.pem;
try_files $uri $uri/ index.html =404;
}

27
docker/packages.yml Normal file
View File

@ -0,0 +1,27 @@
ldppackages:
djangoldp_dashboard: djangoldp_dashboard
djangoldp_account: djangoldp_account
djangoldp_circle: djangoldp_circle
djangoldp_notification: djangoldp_notification
djangoldp_profile: djangoldp_profile
djangoldp_project: djangoldp_project
oidc_provider: 'git+https://github.com/jblemee/django-oidc-provider.git@develop'
server:
site_url: SERVER
allowed_hosts:
- '*'
db_host: database
db_name: postgres
db_user: postgres
db_pass: postgres
smtp_host:
smtp_user:
smtp_pass:
admin_email: test@startinblox.com
admin_name: admin
admin_pass: admin
xmpp_url: https://jabber.happy-dev.fr
jabber_host: 'none'
default_client: SERVER
registration_open: True

1157
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -17,7 +17,11 @@
"watch": "run-p copy:* watch:* serve", "watch": "run-p copy:* watch:* serve",
"watch:css": "npm run build:css && npm run build:css -- -w", "watch:css": "npm run build:css && npm run build:css -- -w",
"watch:js": "babel --watch \"src/scripts/*.js\" -o dist/scripts/index.js", "watch:js": "babel --watch \"src/scripts/*.js\" -o dist/scripts/index.js",
"watch:pug": "pug --watch src/index.pug -o dist/ --obj config.json" "watch:pug": "pug --watch src/index.pug -o dist/ --obj config.json",
"cypress:open": "cypress open",
"cypress:verify": "cypress verify",
"cypress:info": "cypress info",
"test": "cypress run"
}, },
"release": { "release": {
"branches": [ "branches": [
@ -59,5 +63,9 @@
"pug": "^2.0.4", "pug": "^2.0.4",
"pug-cli": "^1.0.0-alpha6", "pug-cli": "^1.0.0-alpha6",
"pushstate-server": "^3.1.0" "pushstate-server": "^3.1.0"
},
"devDependencies": {
"cypress": "^4.5.0",
"cypress-terminal-report": "^1.2.1"
} }
} }