Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • applications/etuc/hubl
  • applications/hubl
  • decentral1se/hubl
  • rngadam/hubl
  • jvtrudel/hubl
  • 3wc/hubl
6 results
Show changes
Commits on Source (1556)
Showing
with 1838 additions and 232 deletions
{
}
**/node_modules
**/config.json
.DS_Store
*.iml
*.swp
.npm
# Test cache
cache
cypress/screenshots
cypress/videos
# Built files
.cache
dist
*-dist
dist-*
# Config specific files
config.json
config.*.json
!config.sample.json
# Client specific files
public/clients/*.css
!public/clients/sample.css
image: node
# workflow
stages:
- deploy_staging
- deploy_production
- build
- test
- deploy
# default image for jobs
default:
image: node:22
## STAGING ##
# cache modules between jobs
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
deploy_paris_stg:
stage: deploy_staging
environment:
name: paris_stg
url: https://staging.test-paris.happy-dev.fr
## BUILD ##
build:
stage: build
before_script:
- npm ci --cache .npm --prefer-offline --only=production
script:
- echo "$APP_CONFIG_PARIS_STG" > config.json
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm install
- cp config.sample.json config.json
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* test-paris@ssh-test-paris.happy-dev.fr:~/staging/www/
only:
- staging
- feat/manager_deploy
artifacts:
when: on_success
expire_in: 1 day
paths:
- dist/
except:
- tags
tags:
- sib
- test
deploy_nantes:
stage: deploy_staging
environment:
name: nantes
url: https://test-nantes.happy-dev.fr
## TESTING ##
test:e2e:
stage: test
image:
name: cypress/included:13.15.2
entrypoint: [""]
services:
- name: ${CI_REGISTRY_IMAGE}/djangoldp:latest
before_script:
# install missing dependencies
- npm install cypress-localstorage-commands
# making sure the process is orphan
- npm run serve -- -l silent &
script:
- echo "$APP_CONFIG_NANTES" > config.json
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm install
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* test-paris@ssh-test-paris.happy-dev.fr:~/staging/www/
only:
- staging
- feat/manager_deploy
- cypress run -e CYPRESS_baseUrl=http://localhost:4173
except:
- tags
tags:
- sib
- test
## PRODUCTION ##
## RELEASE TAGGING ##
deploy_paris:
stage: deploy_production
environment:
name: paris
url: https://test-paris.happy-dev.fr
publish:
stage: deploy
image: node:22
before_script:
- npm install -g semantic-release@24 @semantic-release/gitlab@13
script:
- echo "$APP_CONFIG_PARIS" > config.json
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' > gitlab.key && chmod 600 gitlab.key
- npm install
- npm run build
- scp -i gitlab.key -o StrictHostKeyChecking=no -r dist/* test-paris@ssh-test-paris.happy-dev.fr:~/www/
- semantic-release
only:
- staging
- feat/manager_deploy
when: manual
- master
tags:
- sib
- deploy
/label ~BUG
### How is it now?
(What actually happens)
### How should it be?
(What you should see instead)
### Steps to reproduce
(How one can reproduce the issue - this is very important)
1. Step 1
1. Step 2
1. Step 3
### Relevant logs and/or screenshots
(Paste any relevant logs - please use code blocks to format console output,
logs, and code as it's very hard to read otherwise.)
### Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
If you can, give an estimation of the time needed to fix (in hours)
/estimate Xh
### How to fix a Bug
1. Ask any question you have in the issue comments before starting
1. Create a new branch named issue-${ISSUE_NUMBER}
1. Write a test that is failing to validate that this bug exists
1. If you can not recreate this bug, feel free to submit just a test with clear name and documentation linking to this issue
1. If a test fails indeed, continue to the next step
1. Now, when you have a regression test ready - create a fix for that bug
1. Write any additional tests if needed
1. Document in docstrings in general documentation what was wrong
1. Submit a merge request
## What needs to be done?
/estimate Xh
/label ~feature
## Technical details
Are there any technical details worth mentioning?
## Links
1. Link to user story in wiki
1. other related Gitlab issues
## How to complete
1. How to complete a Feature
1. Ask any question you have in the issue comments before starting
1. Create a new branch named issue-${ISSUE_NUMBER}
1. Implement a simple test to illustrate the desired functionality
1. Write some code to complete this task
1. Write some additional tests to cover edge cases and some possible errors
1. Write documentation about what have you done and why you have done it this way
1. Submit a merge request
## Checklist
<!---
Please, make sure you have changed the topic and also
described briefly what have you done. Thanks!
-->
I have done ...
- [ ] This commits targets only one specific issue
- [ ] The commit message follows our guidelines
- [ ] Tests for the changes have been added
- [ ] Docs have been added or updated
- [ ] I have assigned my architect to review this merge request
- [ ] I have checked how to create a [merge request]()
## Issues
<!---
Which issue this PR closes? It can close only one issue.
Which issues this PR references?
Please, specify all issues.
Format is: Closes #X or Refs #Y
Docs: https://docs.gitlab.com/ee/user/project/issues/closing_issues.html#via-merge-request
-->
## Time spent
<!---
This information is only required for statistics and analysis.
But we need to know exactly how much time you have spent,
please try to be as accurate as possible.
Format is: /spend 1h
Docs: https://docs.gitlab.com/ee/workflow/time_tracking.html
-->
/spend Xh
## Assignee
<!---
You need to assign your architect to review your merge request.
Format: /assign @username
Docs: https://docs.gitlab.com/ee/user/project/quick_actions.html
-->
/assign
## Feedback
<!---
Did you encounter any other problems you want to share with us?
Optional. Feel free to remove this section if you don't have any feedback.
-->
<!--- Thank you for you contribution! -->
{
"branches": [
"master",
{
"name": "beta",
"prerelease": true
}
],
"plugins": [
[
"@semantic-release/commit-analyzer",
{
"preset": "angular",
"releaseRules": [
{
"type": "major",
"release": "major"
},
{
"type": "minor",
"release": "minor"
},
{
"type": "*",
"release": "patch"
}
]
}
],
"@semantic-release/release-notes-generator",
"@semantic-release/gitlab"
]
}
\ No newline at end of file
{
"includePaths": ["node_modules"]
}
\ No newline at end of file
MIT License
Copyright (c) 2018 Startin blox
Copyright (c) 2024-PRESENT Startin blox
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
......
# SiB App
<h1 align="center">
<br>
<a href="https://startinblox.com"><img src="https://cdn.startinblox.com/logos/startinblox.png" alt="Orbit" width="200"></a>
<br>
</h1>
SiB App is the magic tool that allows the Happy Dev network to thrive in a decentralized way.
<h4 align="center">A magic orchestrator that allows any Blox to thrive in a decentralized way, built on top of <a href="https://startinblox.com/" target="_blank">Startin'blox</a>.</h4>
<hr>
<p align="center">
<a href="https://git.startinblox.com/management/product-owners-sync/issues/"><img alt="create a feature request" src="https://img.shields.io/badge/%2B-feature%20request-blue" /></a>
<a href="https://git.startinblox.com/applications/orbit/issues/"><img alt="create an issue" src="https://img.shields.io/badge/%2B-issue-orange" /></a>
<a href="https://git.startinblox.com/applications/orbit/commits/master"><img alt="pipeline status" src="https://git.startinblox.com/applications/hubl/badges/master/pipeline.svg" /></a>
</p>
## Getting Started
......@@ -8,50 +20,38 @@ These instructions will get you a copy of the project up and running on your loc
### Prerequisites
To install SiB App, you'll need:
To install Orbit, you'll need:
* A SIB server with the appropriate modules
* A Prosody Server (with [appropriate modules](https://git.happy-dev.fr/startinblox/prosody/custom-prosody-modules/)
* A [DjangoLDP Server](https://git.startinblox.com/djangoldp-packages/server-manager/) (djangoldp>2)
* A [Prosody Server](https://prosody.im/) (with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/)) (optional)
* A SMTP Server (optional)
* NodeJS on your machine
* NodeJS (>=20) on your machine
Before diving in you have to check your SIB server supports the following LDP packages:
Before diving in you have to check your DjangoLDP Server supports the following LDP packages:
* djangoldp_account: 0.2.14
* djangoldp_circle: 0.1.15
* djangoldp_joboffer: 0.1.1
* djangoldp_notification: 0.1.4
* djangoldp_profile: 0.1.4
* djangoldp_project: 0.1.12
* djangoldp_skill: 0.1.1
* oidc_provider: 'git+https://github.com/jblemee/django-oidc-provider.git@develop'
* djangoldp_account
* djangoldp_uploader
* an oidc_provider (eg: [django-webidoidc-provider](https://git.startinblox.com/djangoldp-packages/django-webidoidc-provider))
Those packages are given with the last stable version tested.
Refer to the [documentation to install a SIB server](https://git.happy-dev.fr/startinblox/devops/doc/wikis/install_sib_server) with this configuration.
Refer to the [documentation to install a DjangoLDP Server](https://docs.startinblox.com/import_documentation/install-djangoldp-server.html) with this configuration.
## Build the application
In order to find your server(s) the client application needs to be assembled with the proper configuration.
Get the code of the SIB app on your machine:
Get the code of the Orbit on your machine:
```bash
git clone ...
cd orbit
npm install
```
Then create a `config.json` with all the the capabilities the SIB App requires. Which are:
* circles
* groups
* joboffers
* projects
* skills
* users
Then create a `config.json` based on your needs, see Mandatory and Optional Modules on this page. For convienence a `config.sample.json` exists in the source.
[The documentation](https://git.happy-dev.fr/startinblox/devops/doc/wikis/build_sib_application) explains the details of this file but for convienence a `config.sample.json` exists in the source.
Then build your new SIB App:
Then build your new Orbit:
```bash
npm run build
......@@ -67,22 +67,1072 @@ Serve, watch files & rebuild on change with this command:
npm run watch
```
## Built With
Notice that you may have to restart the watcher for the config.json and for locales files.
### Multiple config.json
You can have as many `config.*.json` as you need.
Watch on a custom config file:
```bash
CONFIG_PATH='config.customName.json' npm run watch
```
Build with a custom config file:
```bash
CONFIG_PATH='config.customName.json' npm run build
```
## Component, extensions, integrations?
The core concept of Orbit is to allow any webcomponent to integrate with Orbit **and** with any other Orbit-loaded component.
A **component** is a webcomponent, named by its `type` key.
It can have as many `parameters` as it needs. Orbit will translate them to `attributes` if needed (e.g., `dataSrc` => `data-src`), and will try to load any object directly on the component.
A `parameters` can point to `server://` or `federation://`, and Orbit will create a proper local resource for this attribute.
A component can be duplicated as many times as needed; Orbit will try to deduplicate any `route` using the `type` and the `uniq` key.
The `uniq` key is handled by Orbit; you can suggest one, and Orbit will do its best to use it.
An **extension** is a sub-component, it is also a web component, and the parent component will have to handle its integration within its scope.
E.g., an Event component within a Circle component allows plugging Events into one Circle.
An extension can have its own parameters and will not be related if also declared on Orbit's component list.
An **integration** is a link to another component; one component expects another component to behave in its presence.
E.g., a Directory component requires a `routing` integration to be properly handled by the `routing` component.
E.g., an Event component could mention a `dashboard` integration to declare a widget-like Event component, user-manageable, on the Dashboard (not implemented).
A component without an integration to another component is **not** expected to have any interaction with it.
## Mandatory components
By default does not include any component.
On Server: `djangoldp_account`, `djangoldp_upload`, `django-webidoidc-provider` packages
On `config.json`:
```json
{
"client": {
"name": "Localhost",
"logo": "/images/logo.webp",
"server": "http://server"
},
"components": []
}
```
Where:
* `client.name` is the name of your Orbit
* `client.logo` is an URL to an image file
* `client.server` is the main data server of the client
* `components` is your modules declaration registry
### Optional personalisation
On `config.json`:
```json
{
"client": {
"favicon": "/images/favicon.webp",
"css": "/path/to/custom.css",
"defaultAvatar": "/path/to/image.webp",
"i18n": {
"lang": "fr",
"force": "false"
}
}
}
```
Where:
* `client.favicon` is an URL to a distant favicon
* `client.css` is an URL to a distant CSS that'll be the last one loaded by the Orbit. You can use an array of css files
* `client.defaultAvatar` is an URL to a distant image that'll be used instead of the default alien
* `client.i18n.lang` is the fallback langage in case the visitor's browser one does not contain the string
* `client.i18n.force` allows to ignore the visitor's browser langage and force the `client.i18n.lang` one
### Allow to login to your application
Most of other components will need to have an user logged in, if you want to use communities, then scroll back to the User Registration module, else you'll need to activate the Auto Login module:
```json
{
"type": "autoLogin",
"parameters": {
"authority": "http://server.url/"
},
"route": false
}
```
Where:
* `authority` is the OpenID Provider. Usually, if you use `djangoldp_account` it's the same as your djangoldp server.
## Optional components
### Adding components
You can append any module listed bellow to your `components` entry on your `config.json`
Eg. to add the `notification` module:
```json
{
"components": [
{
"type": "notification",
"route": false
}
]
}
```
### About
About is a short page about the technology behind Orbit.
To activate about on Orbit, add this module declaration your `config.json`:
```json
{
"type": "about"
}
```
### Administration
Administration is a minimal modulable admin module for all other ones.
To activate administration on Orbit, add this module declaration your `config.json`:
```json
{
"type": "admin",
"route": false
}
```
### Analytics
Orbit support Google or Matomo as analytics trackers. To use them, add to your `config.json`:
```json
{
"type": "analytics",
"parameters": {
"type": "matomo",
"url": "https://my-personal.matomo.cloud/",
"id": "1"
},
"route": false
}
```
### Circles
Circles define group of users that can chat & share documents togethers.
Community module is mandatory.
To activate them, you need:
On Server: `djangoldp_circle`, `djangoldp_communities`, `djangoldp_notifications` packages
Module declaration, on `config.json`:
```json
{
"type": "circles",
"parameters": {
"dataSrc": "federation://circles/",
"dataSrcJoinable": "federation://circles/joinable/",
"owners": "federation://users/",
"post": "server://circles/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket",
"twoStepCreation": true,
"customMembersDisplayWidget": "tzcld-circle-members-display",
"notRequiredSubtitle": true,
"nextListItem": false
},
"federation": [
"..."
],
"route": "circles"
}
```
Where:
* `dataSrc`: is a container listing every circles
* `dataSrcJoinable`: is a container listing every public & not joined circles for the user
* `post`: is where you create new circles
* `noRender`: will load datas only when it'll be on screen, remove it if you encounter any issue
* `owners`: is your users container which contains valid owners
* `users`: is your users container
* `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/)
* `twoStepCreation` 2 step creat circle with add memebers on second step
* `customMembersDisplayWidget` Use your own widget for display memebers on list
* `notRequiredSubtitle` By default subtitle is required. Can disable it with true value
* `nextListItem` By default false. If true value, enable link to information circle for all item in join or leave lists
#### Circles extensions
You can extend circles with other components, the same way you would add them to your components.
Extensions always inherit from its parent federation.
Actually it support: Events, Resources & Polls.
Can enable option `twoStepCreation` with `true` to directly display an add user form on circle after the firts creation form.
Can define custom widget to display memeber of cicrcle on circle profile. Set parameter `customMembersDisplayWidget` with the name of your custom widget
Eg.:
```json
{
"type": "circles",
"parameters": {
"dataSrc": "federation://circles/",
"dataSrcJoinable": "federation://circles/joinable/",
"owners": "federation://users/",
"post": "server://circles/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket",
"twoStepCreation": false,
"customMembersDisplayWidget": "name-of-widget"
},
"federation": [
"..."
],
"route": "circles",
"extensions": [
{
"type": "events",
"parameters": {
"dataSrc": "federation://events/",
"postUri": "server://events/",
"circles": "federation://circles/",
"typeevents": "federation://typeevents/",
"postTypeUri": "server://typeevents/",
"uploads": "server://upload/"
},
"federation": [
"..."
]
}
]
}
```
### Communities
Communities are an optional layer to add on an Orbit. They add a SOLID representation of one to many group of users on your data server.
If you're upgrading an existing Orbit, you can assign all your local users to a community this way:
```bash
./manage.py create_community --name="My community"
```
Don't forget to set some users as admin of the community from the Django Admin if you want to allow them to create new users from the app.
To activate community on Orbit, add this module declaration your `config.json`:
```json
{
"type": "communities",
"route": false
}
```
#### Activate the community directory
When you work with a federated application, you may want a directory for communities.
You can activate it by changing the route to anything else than false. Some endpoints will also get needed:
```json
{
"type": "communities",
"parameters": {
"addresses": "federation://community-addresses/",
"dataSrc": "federation://communities/",
"uploads": "server://upload/"
},
"route": "communities",
"integration": ["routing"]
}
```
### Dashboard
Dashboard includes card generation from HTML. To activate them, you need:
On Server: `djangoldp_dashboard` packages
Module declaration, on `config.json`:
```json
{
"type": "dashboard",
"parameters": {
"dataSrc": "server://dashboards/",
"noRender": "",
"target": false
},
"route": "dashboard",
"integration": [
"routing"
]
}
```
A [sample fixture](https://git.startinblox.com/djangoldp-packages/djangoldp-dashboard/blob/master/djangoldp_dashboard/fixtures/sample.json) can be loaded with `./manage.py loaddata sample`.
You can have multiple dashboard module, see the [related documentation](https://git.startinblox.com/components/solid-dashboard#having-multiple-dashboard).
You need the routing integration enabled to have a Dashboard.
### Events
The events module includes a listing of upcoming events and the capability to create new ones.
To activate it, you need:
On Server: `djangoldp_event` packages
Module declaration, on `config.json`:
```json
{
"type": "events",
"parameters": {
"dataSrc": "federation://events/",
"postUri": "server://events/",
"circles": "federation://circles/",
"typeevents": "federation://typeevents/",
"postTypeUri": "server://typeevents/",
"pastevents": "visible",
"visiblecheckbox": "visible",
"visibilityregions": "visible",
"uploads": "server://upload/"
},
"federation": [
"..."
]
}
```
Where:
* `parameters.pastevents`: is blank or hidden to display past events
* `parameters.visiblecheckbox`: is blank or hidden to add a checkbox that allow to show events from another website
* `parameters.visibilityregions`: is blank or hidden to have a Region field on events
You can get only future events by using:
```json
"get": "http://server.url/events/future/",
```
### Internationalization
Each client can overwrite langs files with their own or even create custom langs.
#### Overwrite langs
On `config.json`:
```json
{
"type": "lang",
"parameters": {
"name": "fr",
"file": "/path/to/fr.json"
},
"route": false
}
```
Where:
* `parameters.name` allows to use custom client lang file.
* `parameters.file` is the path the the json lang file
#### Custom langs
Needs `client.i18n.force` to `true` and `client.i18n.lang` to the custom lang name.
Your custom JSON file **must** contain every keys, from the template and from every bloxes, prefixed by the blox namespace. See example on `src/locales/fr.json`.
On `config.json`:
```json
{
"client": {
"i18n": {
"lang": "pirate",
"force": "true"
}
},
"components": [
{
"type": "i18n",
"parameters": {
"name": "pirate",
"file": "/path/to/yarr.json"
},
"route": false
}
]
}
```
Where:
* `parameters.name` allows to use custom client lang file.
* `parameters.file` is the path the the json lang file
Setting custom langs will not allow user to choose their own lang.
### Invoices
Invoices allow your Projects to manage invoices
Project is mandatory.
You'll need:
On Server: `djangoldp_invoice` packages
Module declaration, on `config.json`:
```json
{
"type": "projects",
"extensions": [
{
"type": "invoices",
"parameters": {
"uploads": "server://upload/"
}
}
]
}
```
### Job Offers
Job Offers includes a job board with conversation. To activate them
Community module is mandatory.
You'll need:
On Server: `djangoldp_joboffer`, `djangoldp_skill`, `djangoldp_upload`, `djangoldp_conversation` packages
Module declaration, on `config.json`:
```json
{
"type": "job-board",
"parameters": {
"dataSrc": "federation://job-offers/current/",
"dataSrcExpired": "federation://job-offers/expired/",
"postDataSrc": "server://job-offers/",
"noRender": "",
"rangeSkills": "federation://skills/",
"fields": "earnBusinessProviding"
},
"route": "job-offers",
"integration": [
"routing"
]
}
```
Where:
* `parameters.fields`: Optional set of custom fields. Notice that only `earnBusinessProviding` is already handled on djangoldp-joboffer.
You need the routing integration enabled to have a Job Board.
### Notifications
The notification module adds a bell with user's notification list and a badge on each menus with how much notifications are related to this resource. You'll need:
On Server: `djangoldp_notifications` packages
On `config.json`:
```json
{
"type": "notification",
"route": false
}
```
* [Sib-Core](https://git.happy-dev.fr/startinblox/framework/sib-core/) - An awesome new framework!
### One-to-one chat
<!---
## Contributing
One-to-one chat allow your users to chat together on a private channel.
We may add a `CONTRIBUTING.md`
Community & User Directory components are mandatory.
## License
You'll need:
We may add a `LICENSE.md`
Module declaration, on `config.json`:
## Acknowledgments
```json
{
"type": "chat",
"parameters": {
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
}
}
```
Where:
* `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/) configured on.
### Polls
The polls module allows user to create polls. To activate it, you need:
On Server: `djangoldp_polls`, `djangoldp_conversation` packages
On `config.json`:
```json
{
"type": "polls",
"parameters": {
"dataSrc": "http://server.url/polls/",
"post": "http://server.url/polls/",
"pollRangeTags": "http://server.url/tags/",
"pollRangeCircles": "http://server.url/circles/",
"uploads": "http://server.url/upload/",
"displayStartEndDates": false
}
}
```
Where:
* `parameters.displayStartEndDates` display or not the start and the end date of polls
### Project
Project are a private group chat including Customer and Business Provider management.
Community module is mandatory.
To activate them, you need:
On Server: `djangoldp_project` packages
Module declaration, on `config.json`:
```json
{
"type": "projects",
"parameters": {
"captains": "federation://users/",
"circles": "federation://circles/",
"dataSrc": "federation://projects/",
"dataSrcJoinable": "federation://projects/joinable/",
"post": "server://projects/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
},
"route": "projects"
}
```
Where:
* `captains`: is your users container which contains valid captains
* `xmpp` is your [Prosody](https://prosody.im/) with [appropriate modules](https://git.startinblox.com/infra/prosody-modules/) configured on.
### Resources
The resources module includes a listing of indexed resources and the capability to index new ones.
To activate it, you need:
On Server: `djangoldp_resource`, `djangoldp_conversation` packages
Module declaration, on `config.json`:
```json
{
"type": "resources",
"parameters": {
"dataSrc": "http://server.url/resources/",
"post": "http://server.url/resources/",
"types": "http://server.url/types/",
"keywords": "http://server.url/keywords/",
"circles": "http://server.url/circles",
"postTypes": "http://server.url/types/",
"postKeywords": "http://server.url/keywords/",
"uploads": "http://server.url/upload/"
}
}
```
### User registration
The user registration module allows users to self-register.
Community module is mandatory.
If you set `allow_self_registration` on a community, it'll disable the auto-login feature of Orbit and allow your users to self register on your application.
To activate it, you need:
Module declaration, on `config.json`:
```json
{
"type": "registering",
"parameters": {
"dataSrc": "server://open-communities/",
"authority": "http://server.url/",
"authorityName": "your-authority-indentifier"
}
}
```
Where:
* `authority` is the OpenID Provider. Usually, if you use `djangoldp_account` it's the same as your djangoldp server.
* `authorityName` is a visual name of your OpenID Provider
If you add the `allowAnonymous` parameter, it will disable the automatic redirection to login page, and allow your users to navigate the app without being logged in. Activate it like this:
Module declaration, on `config.json`:
```json
{
"type": "registering",
"parameters": {
"authority": "http://server.url/",
"authorityName": "your-authority-indentifier",
"allowAnonymous": true
}
}
```
### Users Directory
Directory includes a listing of each users of your app and editable individual profile. To activate them, you need:
On Server: `djangoldp_skill` packages
Community module is mandatory.
Module declaration, on `config.json`:
```json
{
"type": "directory",
"parameters": {
"dataSrc": "federation://users/",
"rangeSkills": "federation://skills/",
"noRender": "",
"paginateBy": "30",
"uploads": "server://upload/"
},
"federation": [
"..."
],
"route": "members",
"integration": [
"routing"
]
}
```
You need the routing integration enabled to have a Profile Directory.
## Further configuration
### Add link to left menu
```json
{
"type": "link",
"parameters": {
"icon": "icon-docs",
"name": "some.path",
"target": "https://startinblox.com"
},
"route": true
},
```
Where:
* `parameters.icon` is a valid simple line icon
* `parameters.name` is a path to a translation. You can create your own with the lang component
* `parameters.target` is the target URL
### Enfore NPM package version
To enforce a version to get used from NPM, add this to your `config.json`:
```json
"npm": [
{
"package": "@startinblox/core",
"version": "0"
}
],
```
Where:
* `npm[].package` is the package name to include
* `npm[].version` if the version of the package you want to use
* `npm[].attributes` (optional) allows to add attributes to the script element
Package will get required from Skypack CDNs.
#### Use local packages
Use the `npm` parameter on your `config.json`
```json
{
"package": "@startinblox/oidc",
"version": "0",
"path": "./path/to/sib-auth/sib-auth.js"
}
```
### Route generation
Orbit will, by default, generate an unique route for every of your module. You can customize this route by setting a `route` attribute on your module declaration.
Eg. for the Users Directory:
```json
{
"type": "directory",
"parameters": {
"dataSrc": "server://users/",
"skills": "server://skills/",
"uploads": "server://upload/"
},
"route": "profiles"
}
```
Will lead to `http://client.url/profiles` as URL to reach the module instead of the default `http://client.url/directory`.
If you provide no `route`, then Orbit will use the `type` as route view name. If two components share the same `route`, they'll get suffixed with a random unique id.
Some module don't need any route to be active, set `route` to `false` so.
Components can get the route of a module with `window.orbit.getRoute('componentName')`.
#### Routing integration
This setting allow to create a view containing a `solid-*` with every parameters provided without any code.
```json
{
"type": "display",
"parameters": {
"dataSrc": "server://users/",
"fields": "name"
},
"integration": [
"routing"
]
}
```
will provide a view with:
```html
<solid-display data-src="http://server/users/" fields="name"></solid-display>
```
#### Set the default route
By default, Orbit will take a Dashboard as a default route.
You can enforce a component to be the default one by adding `defaultRoute` to its parameters.
Eg.:
```json
{
"type": "directory",
"parameters": {
"dataSrc": "server://users/",
"skills": "server://skills/",
"uploads": "server://upload/"
},
"route": "directory",
"defaultRoute": true
}
```
If there is more than one component with this parameter, it'll be ignored.
### Left Menu
#### Customize left menu
You can enable/disable the left menu.
Add a `"menu"` component to your config
```json
{
"type": "menu",
"parameters": {
"disabled": true
}
}
```
#### Left menu integration
It is possible to automatically add a menu entry on the left. For this:
Add a `"menu"` element to the `integration` config
```json
{
"type": "mycomponent",
"parameters": {
"dataSrc": "server://users/",
"fields": "name"
},
"integration": [
"menu"
]
}
```
Then create a component `solid-mycomponent-menu`. This component will be rendered in the left sidebar.
### Top Menu
#### Customize top menu
You can enable/disable the top menu, switch from text to logo or even change the users' displayed fields.
Add a `"menu-top"` component to your config
```json
{
"type": "menu-top",
"parameters": {
"disabled": false,
"displayLogo": true,
"userCustomField": "first_name",
"classUserCustomField": "text-semibold text-color-heading"
}
}
```
Notice that if you use the code above you may want to fix the CSS in one of your client.css files:
```css
header details[open] summary .labelled-avatar>div {
line-height: 16px;
}
```
#### Top menu integration
It is possible to automatically add a menu entry to the top. For this:
Add a `"menu"` element to the `integration` config
```json
{
"type": "mycomponent",
"parameters": {
"dataSrc": "server://users/",
"fields": "name"
},
"integration": [
"menu-top"
]
}
```
Then create a component `solid-mycomponent-menu-top`. This component will be rendered in the top menu dropdown or the top simple menu.
#### Top simple menu
You can disable the top menu dropdown in favor of a simple list generated from menu-top components only.
```json
{
"client": {
"simpleMenu": true
}
}
```
### Client-side federations
Any parameter of your config.json can take benefits from the source generation:
`server://` will be replaced by the value of `client.server`:
```json
{
"type": "awesome",
"parameters": {
"dataSrc": "server://users/"
}
}
```
`federation://` will generate a virtual federated container linking:
* `client.server`
* `client.servers`
* `component.federation`
* `extension.federation`, if on an extension
```json
{
"client": {
"server": "http://serverA",
"servers": [
"http://serverB"
]
},
"components": [
{
"type": "awesome",
"parameters": {
"dataSrc": "federation://users/"
},
"federation": [
"http://serverC"
]
}
]
}
```
### PWA / Mobile Application
In addition of the manifest configuration, the PWA will use:
* client.i18n.lang
* client.name
* client.shortName
For a better experience, please ensure to provide a complete manifest.
Default minimal manifest configuration:
```json
{
"client": {
"pwa": {
"dir": "ltr",
"icons": [
{
"src": "/pwa/pwa-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/pwa-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/pwa/pwa-maskable-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/pwa/pwa-maskable-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"start_url": "/",
"display": "standalone",
"orientation": "portrait",
"background_color": "#fff",
"theme_color": "#FFFFFF"
}
}
}
```
[Recommended manifest generator](https://favicon.inbrowser.app/tools/favicon-generator)
## Enable/Disable a component
You can enable/disable any component by adding the `disabled` parameter.
```json
{
"type": "xxx",
"parameters": {
"disabled": true
}
}
```
## Replace a component
You can replace any component with the `replacement` integration.
```json
{
"type": "xxx",
"parameters": {
"replacement": "my-custom-component"
},
"integration": ["replacement"]
}
```
Please notice that `my-custom-component` should be loaded from any package within your `npm` object.
## Troubleshooting
### Circles or Projects are missing the @user list
Did you properly created subscriptions on your DjangoLDP's server? You can quickly create them with `./manage.py create_subscriptions`
### I see some technical strings instead of my fallback language strings
By default, the template fallback to `fr` lang, which is the most complete. If you're using your own fallback lang within your configuration, please ensure that this lang is completly translated.
### I see some technical strings instead of my custom language
Using a custom language file does not allow to use the default fallback language.
## Built With
* Maybe some thanks too
* Inspiration
* etc
--->
* [Sib-Core](https://git.startinblox.com/framework/sib-core/) - A SOLID-Compliant framework
:root {
--color-text: #636363;
--color-primary: #FF0055;
--color-secondary: #0068FF;
--color-third: #00E3B4;
--color-heading: #2E3F58;
}
{
"xmpp": "https://jabber.happy-dev.fr/http-bind/",
"authority": "http://localhost:8000/",
"endpoints": {
"circles": "http://localhost:8000/circles/",
"groups": "http://localhost:8000/groups/",
"joboffers": "http://localhost:8000/job-offers/",
"projects": "http://localhost:8000/projects/",
"skills": "http://localhost:8000/skills/",
"users": "http://localhost:8000/users/"
}
}
"client": {
"name": "Sample of a functional Orbit",
"logo": "https://cdn.startinblox.com/logos/webp/startinblox.webp",
"server": "http://localhost:8000",
"css": "server://clients/sample.css"
},
"components": [{
"type": "registering",
"parameters": {
"dataSrc": "server://open-communities/",
"authority": "http://localhost:8000/",
"authorityName": "dataserver"
},
"route": false
},
{
"type": "notification",
"route": false
},
{
"type": "admin",
"route": false
},
{
"type": "about"
},
{
"type": "communities",
"route": false
},
{
"type": "dashboard",
"parameters": {
"dataSrc": "server://dashboards/"
},
"experimental": [
"routing"
]
},
{
"type": "directory",
"parameters": {
"dataSrc": "federation://users/",
"rangeSkills": "federation://skills/",
"paginateBy": "30",
"uploads": "server://upload/"
},
"route": "members",
"experimental": [
"routing"
]
},
{
"type": "job-board",
"parameters": {
"dataSrc": "federation://job-offers/current/",
"dataSrcExpired": "federation://job-offers/expired/",
"postDataSrc": "server://job-offers/",
"rangeSkills": "federation://skills/"
},
"route": "job-offers",
"experimental": [
"routing"
]
},
{
"type": "projects",
"parameters": {
"captains": "federation://users/",
"circles": "federation://circles/",
"dataSrc": "federation://projects/",
"dataSrcJoinable": "federation://projects/joinable/",
"post": "server://projects/",
"users": "federation://users/",
"noRender": "",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
}
},
{
"type": "circles",
"parameters": {
"dataSrc": "federation://circles/",
"dataSrcJoinable": "federation://circles/joinable/",
"owners": "federation://users/",
"post": "server://circles/",
"users": "federation://users/",
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
}
},
{
"type": "chat",
"parameters": {
"xmpp": "wss://xmpp-dev.startinblox.com/xmpp-websocket"
},
"route": "messages"
},
{
"type": "analytics",
"parameters": {
"type": "matomo",
"url": "https://matomo.startinblox.com/",
"id": "2"
},
"route": false
}
]
}
\ No newline at end of file
const { defineConfig } = require('cypress')
module.exports = defineConfig({
defaultCommandTimeout: 2000,
chromeWebSecurity: false,
viewportWidth: 1920,
viewportHeight: 1080,
video: false,
screenshotOnRunFailure: false,
e2e: {
baseUrl: 'http://localhost:4173',
},
})
## Users:
```
- signin
- signup
- create
- listing
- edit
// TO-FIX: Uncomment workaround (blocked by: nested routing bug)
```
## Job Offers:
```
- create
- edit
- listing
// TO-FIX: Search by title and description (blocked by: no search fields available)
- post
// TO-DO: Entire flow (blocked by: no published / unpublished flag)
```
## Channels:
```
- create
- edit
- leave
- retire
- delete
- join
// TO-DO: Join other user created project (blocked by: can't create new user to create a channel)
- invite
// TO-DO
```
## Projects:
```
- create
- edit
- leave
- retire
- delete
// TO-DO: Delete a project (blocked by: no option to delete a project)
- join
// TO-DO: Join other user created project (blocked by: can't create new user to create a project)
- invite
// TO-DO
```
## Breakdown:
```
DONE: 15
TO-FIX: 2
BLOCKED: 4
NOT DONE: 2
```
/// <reference types="Cypress" />
/* globals cy, expect */
context('Create Channel Browser Testing', () => {
before(() => {
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screen', () => cy.userLogin());
it('should login', () => cy.login());
describe('Channel Creation process #1', () => {
let channelName = 'Test Channel ',
description = 'Test Description ',
subtitle = 'Test Subtitle ';
it('should visit the channel creation screen', () => {
cy.visit('/admin-circles-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles-create');
});
});
it('should enter correct channel data', () => {
cy.randomNum().then(num => {
channelName += num;
description += num;
subtitle += num;
cy.get('#admin-circles-create input[name="name"]').clear().type(channelName);
cy.get('#admin-circles-create input[name="name"]').should('have.value', channelName);
cy.get('#admin-circles-create input[name="subtitle"]').clear().type(subtitle);
cy.get('#admin-circles-create input[name="subtitle"]').should('have.value', subtitle);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create channel button', () => {
cy.get('#admin-circles-create input[type="submit"]').click();
});
it('should land on channels list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should land newly created channel on channels list screen', () => {
cy.contains('solid-display-value[name="circle.name"]', channelName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
describe('Channel Creation process #2', () => {
let channelName = 'Test Channel ',
description = 'Test Description ',
subtitle = 'Test Subtitle ';
it('should visit the channel creation screen', () => {
cy.visit('/admin-circles-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles-create');
});
});
it('should enter correct channel data', () => {
cy.randomNum().then(num => {
channelName += num;
description += num;
subtitle += num;
cy.get('#admin-circles-create input[name="name"]').clear().type(channelName);
cy.get('#admin-circles-create input[name="name"]').should('have.value', channelName);
cy.get('#admin-circles-create input[name="subtitle"]').clear().type(subtitle);
cy.get('#admin-circles-create input[name="subtitle"]').should('have.value', subtitle);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create channel button', () => {
cy.get('#admin-circles-create input[type="submit"]').click();
});
it('should land on channels list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should land newly created channel on channels list screen', () => {
cy.contains('solid-display-value[name="circle.name"]', channelName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
describe('Channel Creation process #3', () => {
let channelName = 'Test Channel ',
description = 'Test Description ',
subtitle = 'Test Subtitle ';
it('should visit the channel creation screen', () => {
cy.visit('/admin-circles-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles-create');
});
});
it('should enter correct channel data', () => {
cy.randomNum().then(num => {
channelName += num;
description += num;
subtitle += num;
cy.get('#admin-circles-create input[name="name"]').clear().type(channelName);
cy.get('#admin-circles-create input[name="name"]').should('have.value', channelName);
cy.get('#admin-circles-create input[name="subtitle"]').clear().type(subtitle);
cy.get('#admin-circles-create input[name="subtitle"]').should('have.value', subtitle);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-circles-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create channel button', () => {
cy.get('#admin-circles-create input[type="submit"]').click();
});
it('should land on channels list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should land newly created channel on channels list screen', () => {
cy.contains('solid-display-value[name="circle.name"]', channelName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
});
/// <reference types="Cypress" />
/* globals cy, expect */
context('Create Job Offer Browser Testing', () => {
let jobDate = '',
jobTitle = 'Test Job Offer ',
description = 'Test Description ';
before(() => {
cy.nextYear(1).then(year => {
jobDate = year + '-12-31';
});
cy.randomNum().then(num => {
jobTitle += num;
description += num;
});
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screen', () => cy.userLogin());
it('should login', () => cy.login());
describe('Job Offer Creation process', () => {
it('should visit the job offer creation screen', () => {
cy.visit('/job-offers/job-offers-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/job-offers/job-offers-create');
});
});
it('should enter correct job offer data', () => {
cy.get('#job-offers-create input[name="closingDate"]').clear().type(jobDate);
cy.get('#job-offers-create input[name="closingDate"]').should('have.value', jobDate);
cy.get('#job-offers-create input[name="title"]').clear().type(jobTitle);
cy.get('#job-offers-create input[name="title"]').should('have.value', jobTitle);
cy.get('#job-offers-create textarea[name="description"]').clear().type(description);
cy.get('#job-offers-create textarea[name="description"]').should('have.value', description);
});
it('should click on create job offer button', () => {
cy.get('#job-offers-create input[type="submit"]').click();
});
it('should land on job offers list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/job-offers');
});
});
it('should land newly created job offer on job offers list screen', () => {
cy.contains('solid-display-value[name="title"]', jobTitle).should("exist");
cy.contains('solid-display-value[name="description"]', description).should("exist");
});
});
});
/// <reference types="Cypress" />
/* globals cy, expect */
context('Create Project Browser Testing', () => {
before(() => {
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screen', () => cy.userLogin());
it('should login', () => cy.login());
describe('Project Creation process #1', () => {
let projectName = 'Test Project ',
customerName = 'Test Customer ',
description = 'Test Description ';
it('should visit the project creation screen', () => {
cy.visit('/admin-projects-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-projects-create');
});
});
it('should enter correct project data', () => {
cy.randomNum().then(num => {
projectName += num;
customerName += num;
description += num;
cy.get('#admin-projects-create input[name="customer.name"]').clear().type(customerName);
cy.get('#admin-projects-create input[name="customer.name"]').should('have.value', customerName);
cy.get('#admin-projects-create input[name="name"]').clear().type(projectName);
cy.get('#admin-projects-create input[name="name"]').should('have.value', projectName);
cy.get('#admin-projects-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-projects-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create project button', () => {
cy.get('#admin-projects-create input[type="submit"]').click();
});
it('should land on projects list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-projects');
});
});
it('should land newly created project on projects list screen', () => {
cy.contains('solid-display-value[name="project.name"]', projectName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
describe('Project Creation process #2', () => {
let projectName = 'Test Project ',
customerName = 'Test Customer ',
description = 'Test Description ';
it('should visit the project creation screen', () => {
cy.visit('/admin-projects-create');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-projects-create');
});
});
it('should enter correct project data', () => {
cy.randomNum().then(num => {
projectName += num;
customerName += num;
description += num;
cy.get('#admin-projects-create input[name="customer.name"]').clear().type(customerName);
cy.get('#admin-projects-create input[name="customer.name"]').should('have.value', customerName);
cy.get('#admin-projects-create input[name="name"]').clear().type(projectName);
cy.get('#admin-projects-create input[name="name"]').should('have.value', projectName);
cy.get('#admin-projects-create [name="description"] [contenteditable="true"]').clear().type(description);
cy.get('#admin-projects-create [name="description"] [contenteditable="true"]').should('have.value', description);
});
});
it('should click on create project button', () => {
cy.get('#admin-projects-create input[type="submit"]').click();
});
it('should land on projects list screen', () => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-projects');
});
});
it('should land newly created project on projects list screen', () => {
cy.contains('solid-display-value[name="project.name"]', projectName).should("exist");
cy.fixture('admin.json').then(admin => {
cy.contains('solid-display-value[name="username"]', admin.username).should("exist");
});
});
});
});
/// <reference types="Cypress" />
/* globals cy, expect */
// Unable to create an user without a community. Unable to create community from app
// context('Create User Browser Testing', () => {
// let firstName = 'First ',
// lastName = 'Last ',
// username = 'testuser_creation_',
// email = '';
// before(() => {
// cy.randomNum().then(num => {
// firstName += num;
// lastName += num;
// username += num;
// email = username + '@testemail.com';
// });
// cy.clearLocalStorageSnapshot();
// cy.clearLocalStorage({ domain: null });
// cy.clearCookies({ domain: null });
// });
// beforeEach(() => cy.restoreLocalStorage());
// afterEach(() => cy.saveLocalStorage());
// it('should visit user login screen', () => cy.userLogin());
// it('should login', () => cy.login());
// describe('User Creation process', () => {
// it('should visit the user creation screen', () => {
// cy.visit('/admin-users-create');
// cy.location().should((loc) => {
// expect(loc.pathname).to.eq('/admin-users-create');
// });
// });
// it('should enter correct user data', () => {
// cy.get('#admin-users-create input[name="first_name"]').clear().type(firstName);
// cy.get('#admin-users-create input[name="first_name"]').should('have.value', firstName);
// cy.get('#admin-users-create input[name="last_name"]').clear().type(lastName);
// cy.get('#admin-users-create input[name="last_name"]').should('have.value', lastName);
// cy.get('#admin-users-create input[name="username"]').clear().type(username);
// cy.get('#admin-users-create input[name="username"]').should('have.value', username);
// cy.get('#admin-users-create input[name="email"]').clear().type(email);
// cy.get('#admin-users-create input[name="email"]').should('have.value', email);
// });
// it('should click on create user button', () => {
// cy.get('#admin-users-create input[type="submit"]').click();
// });
// it('should land on users list screen', () => {
// cy.location().should((loc) => {
// expect(loc.pathname).to.eq('/admin-users');
// });
// });
// it('should land newly created user on users list screen', () => {
// cy.contains('solid-display-value[name="name"]', firstName + ' ' + lastName).should("exist");
// cy.contains('solid-display-value[name="username"]', username).should("exist");
// });
// });
// });
/// <reference types="Cypress" />
/* globals cy, expect */
context('Delete Channel Browser Testing', () => {
let menuQuery = [
'solid-display.circle-tab',
'solid-display:last-child',
'solid-display[order-by="name"]'
],
menuCountQuery = [
'solid-display.circle-tab',
'solid-display',
'solid-display[order-by="name"]'
];
before(() => {
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screend', () => cy.userLogin());
it('should login', () => cy.login());
describe('Channel Retirement process', () => {
let channelsLength;
it('should visit the channels list screen', () => {
cy.visit('/admin-circles');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should visit the last channel edit screen', () => {
cy.get(menuQuery.join(' '))
.invoke('attr', 'data-src')
.then(url => cy.encodeUrl(url).then(id => {
cy.visit('/circles/@' + id + '/circles-information');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/circles/@' + id + '/circles-information');
});
}));
});
it('should count the number of joined channels', () => {
cy.get(menuCountQuery.join(' ')).its('length').then(length => channelsLength = length);
});
it('should click button to retire the channel', () => {
cy.get('#circles-profile solid-delete[data-label="Supprimer le canal"] button').click();
});
it('should stay on channel edit screen', () => {
cy.get(menuQuery.join(' '))
.invoke('attr', 'data-src')
.then(url => cy.encodeUrl(url).then(id => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/circles/@' + id + '/circles-information');
});
}));
});
it('should check if channel was retired', () => {
cy.get(menuCountQuery.join(' ')).its('length').should(length => {
expect(length).to.eq(channelsLength - 1);
});
});
});
});
/// <reference types="Cypress" />
/* globals cy, expect */
context('Edit Channel Browser Testing', () => {
let channelName = 'Edited Test Channel ',
description = 'Edited Test Description ',
menuQuery = [
'solid-display.circle-tab',
'solid-display:last-child',
'solid-display[order-by="name"]'
];
before(() => {
cy.randomNum().then(num => {
channelName += num;
description += num;
});
cy.clearLocalStorageSnapshot();
cy.clearLocalStorage({ domain: null });
cy.clearCookies({ domain: null });
});
beforeEach(() => cy.restoreLocalStorage());
afterEach(() => cy.saveLocalStorage());
it('should visit user login screen', () => cy.userLogin());
it('should login', () => cy.login());
describe('Channel Edition process', () => {
it('should visit the channel list screen', () => {
cy.visit('/admin-circles');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/admin-circles');
});
});
it('should visit the last channel edit screen', () => {
cy.get(menuQuery.join(' '))
.invoke('attr', 'data-src')
.then(url => cy.encodeUrl(url).then(id => {
cy.visit('/circles/@' + id + '/circles-information/circles-edit');
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/circles/@' + id + '/circles-information/circles-edit');
});
}));
});
it('should enter new channel data', () => {
cy.get('#circles-edit input[name="name"]').clear().type(channelName);
cy.get('#circles-edit input[name="name"]').should('have.value', channelName);
cy.get('#circles-edit input[name="description"]').clear().type(description);
cy.get('#circles-edit input[name="description"]').should('have.value', description);
});
it('should click button to save the channel', () => {
cy.get('#circles-edit input[value="Enregistrer"]').click();
});
it('should land on channel information screen', () => {
cy.get(menuQuery.join(' '))
.invoke('attr', 'data-src')
.then(url => cy.encodeUrl(url).then(id => {
cy.location().should((loc) => {
expect(loc.pathname).to.eq('/circles/@' + id + '/circles-information');
});
}));
});
it('should show edited channel data on channel information screen', () => {
cy.contains('solid-display-value[name="name"]', channelName).should("exist");
cy.contains('solid-display-value[name="description"]', description).should("exist");
});
});
});