Merge branch 'bugs' into 'main'
Various bugs Closes #956, #949, #954, #947 et #948 See merge request framasoft/mobilizon!1127
This commit is contained in:
commit
50e9f463ed
@ -190,6 +190,30 @@ pages:
|
|||||||
services:
|
services:
|
||||||
- docker:stable-dind
|
- docker:stable-dind
|
||||||
cache: {}
|
cache: {}
|
||||||
|
tags:
|
||||||
|
- "privileged"
|
||||||
|
|
||||||
|
build-docker-main:
|
||||||
|
<<: *docker
|
||||||
|
rules:
|
||||||
|
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
|
||||||
|
when: never
|
||||||
|
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||||
|
before_script:
|
||||||
|
# Login to DockerHub
|
||||||
|
- mkdir -p ~/.docker
|
||||||
|
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$CI_REGISTRY_AUTH\",\"email\":\"$CI_REGISTRY_EMAIL\"}}}" > ~/.docker/config.json
|
||||||
|
script:
|
||||||
|
- docker build -t framasoft/mobilizon:main -f docker/production/Dockerfile .
|
||||||
|
- docker push framasoft/mobilizon:main
|
||||||
|
|
||||||
|
build-docker-tag:
|
||||||
|
<<: *docker
|
||||||
|
rules: &tag-rules
|
||||||
|
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
|
||||||
|
when: never
|
||||||
|
- if: $CI_COMMIT_TAG
|
||||||
|
timeout: 3 hours
|
||||||
before_script:
|
before_script:
|
||||||
# Install buildx
|
# Install buildx
|
||||||
- wget https://github.com/docker/buildx/releases/download/v0.6.3/buildx-v0.6.3.linux-amd64
|
- wget https://github.com/docker/buildx/releases/download/v0.6.3/buildx-v0.6.3.linux-amd64
|
||||||
@ -210,29 +234,9 @@ pages:
|
|||||||
docker buildx build
|
docker buildx build
|
||||||
--push
|
--push
|
||||||
--platform linux/amd64,linux/arm64,linux/arm
|
--platform linux/amd64,linux/arm64,linux/arm
|
||||||
-t $DOCKER_IMAGE_NAME
|
-t framasoft/mobilizon:$CI_COMMIT_TAG
|
||||||
|
-t framasoft/mobilizon:latest
|
||||||
-f docker/production/Dockerfile .
|
-f docker/production/Dockerfile .
|
||||||
tags:
|
|
||||||
- "privileged"
|
|
||||||
timeout: 3 hours
|
|
||||||
|
|
||||||
build-docker-main:
|
|
||||||
<<: *docker
|
|
||||||
rules:
|
|
||||||
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
|
|
||||||
when: never
|
|
||||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
|
||||||
variables:
|
|
||||||
DOCKER_IMAGE_NAME: framasoft/mobilizon:main
|
|
||||||
|
|
||||||
build-docker-tag:
|
|
||||||
<<: *docker
|
|
||||||
rules: &tag-rules
|
|
||||||
- if: '$CI_PROJECT_NAMESPACE != "framasoft"'
|
|
||||||
when: never
|
|
||||||
- if: $CI_COMMIT_TAG
|
|
||||||
variables:
|
|
||||||
DOCKER_IMAGE_NAME: framasoft/mobilizon:$CI_COMMIT_TAG
|
|
||||||
|
|
||||||
# Packaging app for amd64
|
# Packaging app for amd64
|
||||||
package-app:
|
package-app:
|
||||||
|
@ -34,7 +34,7 @@ In order to keep the release tarballs light, the geographic timezone data is not
|
|||||||
sudo -u mobilizon curl -L 'https://packages.joinmobilizon.org/tz_world/timezones-geodata.dets' -o /var/lib/mobilizon/timezones/timezones-geodata.dets
|
sudo -u mobilizon curl -L 'https://packages.joinmobilizon.org/tz_world/timezones-geodata.dets' -o /var/lib/mobilizon/timezones/timezones-geodata.dets
|
||||||
```
|
```
|
||||||
|
|
||||||
In both cases, ~700Mio of disk will be used. You may use the following configuration to specify where the data is expected:
|
In both cases, ~700Mio of disk will be used. You may use the following configuration to specify where the data is expected if you decide to change it from the default location (`/var/lib/mobilizon/timezones`) :
|
||||||
```elixir
|
```elixir
|
||||||
config :tz_world, data_dir: "/some/place"
|
config :tz_world, data_dir: "/some/place"
|
||||||
```
|
```
|
||||||
|
@ -30,7 +30,8 @@ COPY rel ./rel
|
|||||||
COPY support ./support
|
COPY support ./support
|
||||||
COPY --from=assets ./priv/static ./priv/static
|
COPY --from=assets ./priv/static ./priv/static
|
||||||
|
|
||||||
RUN mix phx.digest.clean --all \
|
RUN mix tz_world.update \
|
||||||
|
&& mix phx.digest.clean --all \
|
||||||
&& mix release
|
&& mix release
|
||||||
|
|
||||||
# Finally setup the app
|
# Finally setup the app
|
||||||
|
@ -29,7 +29,6 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
"@typescript-eslint/no-explicit-any": "off",
|
"@typescript-eslint/no-explicit-any": "off",
|
||||||
"cypress/no-unnecessary-waiting": "off",
|
|
||||||
"vue/max-len": [
|
"vue/max-len": [
|
||||||
"off",
|
"off",
|
||||||
{
|
{
|
||||||
|
@ -1 +1,6 @@
|
|||||||
{}
|
{
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": false,
|
||||||
|
"bracketSpacing": true
|
||||||
|
}
|
||||||
|
@ -17,17 +17,25 @@
|
|||||||
"@apollo/client": "^3.3.16",
|
"@apollo/client": "^3.3.16",
|
||||||
"@mdi/font": "^6.1.95",
|
"@mdi/font": "^6.1.95",
|
||||||
"@tiptap/core": "^2.0.0-beta.41",
|
"@tiptap/core": "^2.0.0-beta.41",
|
||||||
"@tiptap/extension-blockquote": "^2.0.0-beta.6",
|
"@tiptap/extension-blockquote": "^2.0.0-beta.25",
|
||||||
|
"@tiptap/extension-bold": "^2.0.0-beta.24",
|
||||||
"@tiptap/extension-bubble-menu": "^2.0.0-beta.9",
|
"@tiptap/extension-bubble-menu": "^2.0.0-beta.9",
|
||||||
"@tiptap/extension-character-count": "^2.0.0-beta.5",
|
"@tiptap/extension-bullet-list": "^2.0.0-beta.23",
|
||||||
"@tiptap/extension-history": "^2.0.0-beta.5",
|
"@tiptap/extension-document": "^2.0.0-beta.15",
|
||||||
|
"@tiptap/extension-dropcursor": "^2.0.0-beta.25",
|
||||||
|
"@tiptap/extension-gapcursor": "^2.0.0-beta.33",
|
||||||
|
"@tiptap/extension-heading": "^2.0.0-beta.23",
|
||||||
|
"@tiptap/extension-history": "^2.0.0-beta.21",
|
||||||
"@tiptap/extension-image": "^2.0.0-beta.6",
|
"@tiptap/extension-image": "^2.0.0-beta.6",
|
||||||
|
"@tiptap/extension-italic": "^2.0.0-beta.24",
|
||||||
"@tiptap/extension-link": "^2.0.0-beta.8",
|
"@tiptap/extension-link": "^2.0.0-beta.8",
|
||||||
"@tiptap/extension-list-item": "^2.0.0-beta.6",
|
"@tiptap/extension-list-item": "^2.0.0-beta.19",
|
||||||
"@tiptap/extension-mention": "^2.0.0-beta.42",
|
"@tiptap/extension-mention": "^2.0.0-beta.42",
|
||||||
"@tiptap/extension-ordered-list": "^2.0.0-beta.6",
|
"@tiptap/extension-ordered-list": "^2.0.0-beta.24",
|
||||||
|
"@tiptap/extension-paragraph": "^2.0.0-beta.22",
|
||||||
|
"@tiptap/extension-strike": "^2.0.0-beta.26",
|
||||||
|
"@tiptap/extension-text": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-underline": "^2.0.0-beta.7",
|
"@tiptap/extension-underline": "^2.0.0-beta.7",
|
||||||
"@tiptap/starter-kit": "^2.0.0-beta.37",
|
|
||||||
"@tiptap/vue-2": "^2.0.0-beta.21",
|
"@tiptap/vue-2": "^2.0.0-beta.21",
|
||||||
"@vue-a11y/announcer": "^2.1.0",
|
"@vue-a11y/announcer": "^2.1.0",
|
||||||
"@vue-a11y/skip-to": "^2.1.2",
|
"@vue-a11y/skip-to": "^2.1.2",
|
||||||
@ -78,22 +86,19 @@
|
|||||||
"@types/sanitize-html": "^2.5.0",
|
"@types/sanitize-html": "^2.5.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||||
"@typescript-eslint/parser": "^5.3.0",
|
"@typescript-eslint/parser": "^5.3.0",
|
||||||
"@vue/cli-plugin-babel": "~5.0.0-rc.0",
|
"@vue/cli-plugin-babel": "~5.0.0-rc.1",
|
||||||
"@vue/cli-plugin-e2e-cypress": "~5.0.0-rc.0",
|
"@vue/cli-plugin-eslint": "~5.0.0-rc.1",
|
||||||
"@vue/cli-plugin-eslint": "~5.0.0-rc.0",
|
"@vue/cli-plugin-pwa": "~5.0.0-rc.1",
|
||||||
"@vue/cli-plugin-pwa": "~5.0.0-rc.0",
|
"@vue/cli-plugin-router": "~5.0.0-rc.1",
|
||||||
"@vue/cli-plugin-router": "~5.0.0-rc.0",
|
"@vue/cli-plugin-typescript": "~5.0.0-rc.1",
|
||||||
"@vue/cli-plugin-typescript": "~5.0.0-rc.0",
|
"@vue/cli-plugin-unit-jest": "~5.0.0-rc.1",
|
||||||
"@vue/cli-plugin-unit-jest": "~5.0.0-rc.0",
|
"@vue/cli-service": "~5.0.0-rc.1",
|
||||||
"@vue/cli-service": "~5.0.0-rc.0",
|
|
||||||
"@vue/eslint-config-typescript": "^9.0.0",
|
"@vue/eslint-config-typescript": "^9.0.0",
|
||||||
"@vue/test-utils": "^1.1.0",
|
"@vue/test-utils": "^1.1.0",
|
||||||
"@vue/vue2-jest": "^27.0.0-alpha.3",
|
"@vue/vue2-jest": "^27.0.0-alpha.3",
|
||||||
"@vue/vue3-jest": "^27.0.0-alpha.1",
|
"@vue/vue3-jest": "^27.0.0-alpha.1",
|
||||||
"cypress": "^8.3.0",
|
|
||||||
"eslint": "^8.2.0",
|
"eslint": "^8.2.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-cypress": "^2.10.3",
|
|
||||||
"eslint-plugin-import": "^2.20.2",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
"eslint-plugin-prettier": "^4.0.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-vue": "^8.0.3",
|
"eslint-plugin-vue": "^8.0.3",
|
||||||
|
@ -195,10 +195,18 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
|
import { Component, Prop, Vue, Watch } from "vue-property-decorator";
|
||||||
import { Editor, EditorContent, BubbleMenu } from "@tiptap/vue-2";
|
import { Editor, EditorContent, BubbleMenu } from "@tiptap/vue-2";
|
||||||
import StarterKit from "@tiptap/starter-kit";
|
import Blockquote from "@tiptap/extension-blockquote";
|
||||||
|
import BulletList from "@tiptap/extension-bullet-list";
|
||||||
|
import Heading from "@tiptap/extension-heading";
|
||||||
import Document from "@tiptap/extension-document";
|
import Document from "@tiptap/extension-document";
|
||||||
import Paragraph from "@tiptap/extension-paragraph";
|
import Paragraph from "@tiptap/extension-paragraph";
|
||||||
|
import Bold from "@tiptap/extension-bold";
|
||||||
|
import Italic from "@tiptap/extension-italic";
|
||||||
|
import Strike from "@tiptap/extension-strike";
|
||||||
import Text from "@tiptap/extension-text";
|
import Text from "@tiptap/extension-text";
|
||||||
|
import Dropcursor from "@tiptap/extension-dropcursor";
|
||||||
|
import Gapcursor from "@tiptap/extension-gapcursor";
|
||||||
|
import History from "@tiptap/extension-history";
|
||||||
import { IActor, IPerson, usernameWithDomain } from "../types/actor";
|
import { IActor, IPerson, usernameWithDomain } from "../types/actor";
|
||||||
import CustomImage from "./Editor/Image";
|
import CustomImage from "./Editor/Image";
|
||||||
import { UPLOAD_MEDIA } from "../graphql/upload";
|
import { UPLOAD_MEDIA } from "../graphql/upload";
|
||||||
@ -210,7 +218,6 @@ import OrderedList from "@tiptap/extension-ordered-list";
|
|||||||
import ListItem from "@tiptap/extension-list-item";
|
import ListItem from "@tiptap/extension-list-item";
|
||||||
import Underline from "@tiptap/extension-underline";
|
import Underline from "@tiptap/extension-underline";
|
||||||
import Link from "@tiptap/extension-link";
|
import Link from "@tiptap/extension-link";
|
||||||
import CharacterCount from "@tiptap/extension-character-count";
|
|
||||||
import { AutoDir } from "./Editor/Autodir";
|
import { AutoDir } from "./Editor/Autodir";
|
||||||
import sanitizeHtml from "sanitize-html";
|
import sanitizeHtml from "sanitize-html";
|
||||||
|
|
||||||
@ -269,7 +276,9 @@ export default class EditorComponent extends Vue {
|
|||||||
transformPastedHTML: this.transformPastedHTML,
|
transformPastedHTML: this.transformPastedHTML,
|
||||||
},
|
},
|
||||||
extensions: [
|
extensions: [
|
||||||
StarterKit,
|
Blockquote,
|
||||||
|
BulletList,
|
||||||
|
Heading,
|
||||||
Document,
|
Document,
|
||||||
Paragraph,
|
Paragraph,
|
||||||
Text,
|
Text,
|
||||||
@ -279,12 +288,15 @@ export default class EditorComponent extends Vue {
|
|||||||
CustomImage,
|
CustomImage,
|
||||||
AutoDir,
|
AutoDir,
|
||||||
Underline,
|
Underline,
|
||||||
|
Bold,
|
||||||
|
Italic,
|
||||||
|
Strike,
|
||||||
|
Dropcursor,
|
||||||
|
Gapcursor,
|
||||||
|
History,
|
||||||
Link.configure({
|
Link.configure({
|
||||||
HTMLAttributes: { target: "_blank", rel: "noopener noreferrer ugc" },
|
HTMLAttributes: { target: "_blank", rel: "noopener noreferrer ugc" },
|
||||||
}),
|
}),
|
||||||
CharacterCount.configure({
|
|
||||||
limit: this.maxSize,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
injectCSS: false,
|
injectCSS: false,
|
||||||
content: this.value,
|
content: this.value,
|
||||||
|
@ -5,10 +5,14 @@
|
|||||||
|
|
||||||
.ProseMirror {
|
.ProseMirror {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
}
|
||||||
|
.ProseMirror {
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
white-space: break-spaces;
|
||||||
-webkit-font-variant-ligatures: none;
|
-webkit-font-variant-ligatures: none;
|
||||||
font-variant-ligatures: none;
|
font-variant-ligatures: none;
|
||||||
|
font-feature-settings: "liga" 0; /* the above doesn't seem to work in Edge */
|
||||||
|
|
||||||
& [contenteditable="false"] {
|
& [contenteditable="false"] {
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
@ -16,14 +20,22 @@
|
|||||||
& [contenteditable="false"] [contenteditable="true"] {
|
& [contenteditable="false"] [contenteditable="true"] {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
pre {
|
& pre {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
img.ProseMirror-separator {
|
||||||
|
display: inline !important;
|
||||||
|
border: none !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
width: 1px !important;
|
||||||
|
height: 1px !important;
|
||||||
|
}
|
||||||
.ProseMirror-gapcursor {
|
.ProseMirror-gapcursor {
|
||||||
display: none;
|
display: none;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
@ -40,16 +52,17 @@
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ProseMirror-hideselection * {
|
.ProseMirror-hideselection {
|
||||||
&::selection {
|
*::selection {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
&::-moz-selection {
|
*::-moz-selection {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
* {
|
||||||
caret-color: transparent;
|
caret-color: transparent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.ProseMirror-focused .ProseMirror-gapcursor {
|
.ProseMirror-focused .ProseMirror-gapcursor {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
icon="magnify"
|
icon="magnify"
|
||||||
type="search"
|
type="search"
|
||||||
id="search"
|
id="search"
|
||||||
|
ref="autocompleteSearchInput"
|
||||||
:value="search"
|
:value="search"
|
||||||
@input="debouncedUpdateSearchQuery"
|
@input="debouncedUpdateSearchQuery"
|
||||||
dir="auto"
|
dir="auto"
|
||||||
@ -276,6 +277,9 @@ const GEOHASH_DEPTH = 9; // put enough accuracy, radius will be used anyway
|
|||||||
update(data) {
|
update(data) {
|
||||||
this.searchEvents = data.searchEvents;
|
this.searchEvents = data.searchEvents;
|
||||||
this.searchGroups = data.searchGroups;
|
this.searchGroups = data.searchGroups;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
this.$refs.autocompleteSearchInput?.focus();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
currentUser: CURRENT_USER_CLIENT,
|
currentUser: CURRENT_USER_CLIENT,
|
||||||
@ -362,6 +366,7 @@ export default class Search extends Vue {
|
|||||||
|
|
||||||
$refs!: {
|
$refs!: {
|
||||||
aac: FullAddressAutoComplete;
|
aac: FullAddressAutoComplete;
|
||||||
|
autocompleteSearchInput: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
data(): Record<string, unknown> {
|
data(): Record<string, unknown> {
|
||||||
|
@ -160,10 +160,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div
|
<div
|
||||||
class="
|
class="column is-one-third-desktop is-offset-one-third-desktop"
|
||||||
column
|
|
||||||
is-one-third-desktop is-offset-one-third-desktop
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
<h1 class="title">
|
<h1 class="title">
|
||||||
{{ $t("Deleting your Mobilizon account") }}
|
{{ $t("Deleting your Mobilizon account") }}
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
env: {
|
|
||||||
jest: true,
|
|
||||||
"cypress/globals": true,
|
|
||||||
},
|
|
||||||
extends: ["plugin:cypress/recommended"],
|
|
||||||
plugins: ["cypress"],
|
|
||||||
};
|
|
@ -1,13 +0,0 @@
|
|||||||
export default class ElementCount {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
constructor(selector, count) {
|
|
||||||
this.message = `Testing if element <${selector}> has count: ${count}`;
|
|
||||||
this.expected = count;
|
|
||||||
this.pass = (val) => val === count;
|
|
||||||
this.value = (res) => res.value;
|
|
||||||
function evaluator(_selector) {
|
|
||||||
return document.querySelectorAll(_selector).length;
|
|
||||||
}
|
|
||||||
this.command = (cb) => this.api.execute(evaluator, [selector], cb);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
// https://docs.cypress.io/guides/guides/plugins-guide.html
|
|
||||||
|
|
||||||
// if you need a custom webpack configuration
|
|
||||||
// you can uncomment the following import
|
|
||||||
// and then use the `file:preprocessor` event
|
|
||||||
// as explained in the cypress docs
|
|
||||||
// https://docs.cypress.io/api/plugins/preprocessors-api.html#Examples
|
|
||||||
|
|
||||||
module.exports = (on, config) => {
|
|
||||||
// on('file:preprocessor', webpack({
|
|
||||||
// webpackOptions: require('@vue/cli-service/webpack.config'),
|
|
||||||
// watchOptions: {}
|
|
||||||
// }))
|
|
||||||
|
|
||||||
return {
|
|
||||||
...config,
|
|
||||||
fixturesFolder: "tests/e2e/fixtures",
|
|
||||||
integrationFolder: "tests/e2e/specs",
|
|
||||||
screenshotsFolder: "tests/e2e/screenshots",
|
|
||||||
videosFolder: "tests/e2e/videos",
|
|
||||||
supportFile: "tests/e2e/support/index.js",
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,5 +0,0 @@
|
|||||||
// Set the en-US language just in case
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export default function (window) {
|
|
||||||
Object.defineProperty(window.navigator, "language", { value: "en-US" });
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// https://docs.cypress.io/api/introduction/api.html
|
|
||||||
import onBeforeLoad from "./browser-language";
|
|
||||||
|
|
||||||
describe("Homepage", () => {
|
|
||||||
it("Checks the footer", () => {
|
|
||||||
cy.visit("/", { onBeforeLoad });
|
|
||||||
cy.get("#mobilizon").find("footer").contains("The Mobilizon Contributors");
|
|
||||||
|
|
||||||
cy.contains("About")
|
|
||||||
.should("have.attr", "href")
|
|
||||||
.and("eq", "https://joinmobilizon.org");
|
|
||||||
|
|
||||||
cy.contains("License")
|
|
||||||
.should("have.attr", "href")
|
|
||||||
.and("eq", "https://framagit.org/framasoft/mobilizon/blob/main/LICENSE");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to register from the hero section", () => {
|
|
||||||
cy.visit("/", { onBeforeLoad });
|
|
||||||
|
|
||||||
cy.get(".hero-body").contains("Sign up").click();
|
|
||||||
cy.url().should("include", "/register/user");
|
|
||||||
});
|
|
||||||
it("Tries to register from the navbar", () => {
|
|
||||||
cy.visit("/", { onBeforeLoad });
|
|
||||||
|
|
||||||
cy.get("nav.navbar").contains("Sign up").click();
|
|
||||||
cy.url().should("include", "/register/user");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to connect from the navbar", () => {
|
|
||||||
cy.visit("/", { onBeforeLoad });
|
|
||||||
|
|
||||||
cy.get("nav.navbar").contains("Log in").click();
|
|
||||||
cy.url().should("include", "/login");
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,54 +0,0 @@
|
|||||||
beforeEach(() => {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Events", () => {
|
|
||||||
it("Shows my current events", () => {
|
|
||||||
const EVENT = { title: "My first event" };
|
|
||||||
|
|
||||||
cy.loginUser();
|
|
||||||
cy.visit("/events/me");
|
|
||||||
cy.contains(".message.is-danger", "No events found");
|
|
||||||
cy.contains(".navbar-item", "Create").click();
|
|
||||||
|
|
||||||
cy.url().should("include", "create");
|
|
||||||
cy.get(".field").first().find("input").type(EVENT.title);
|
|
||||||
cy.get(".field").eq(1).find("input").type("my tag, holo{enter}");
|
|
||||||
cy.get(".field").eq(2).find(".datepicker .dropdown-trigger").click();
|
|
||||||
|
|
||||||
cy.get(".field")
|
|
||||||
.eq(3)
|
|
||||||
.find(".pagination-list .control")
|
|
||||||
.first()
|
|
||||||
.find(".select select")
|
|
||||||
.select("September");
|
|
||||||
cy.get(".field")
|
|
||||||
.eq(3)
|
|
||||||
.find(".pagination-list .control")
|
|
||||||
.last()
|
|
||||||
.find(".select select")
|
|
||||||
.select("2021");
|
|
||||||
cy.get(".field").eq(3).contains(".datepicker-cell", "15").click();
|
|
||||||
|
|
||||||
cy.contains(".button.is-primary", "Create my event").click();
|
|
||||||
cy.url().should("include", "/events/");
|
|
||||||
cy.contains(".title", EVENT.title);
|
|
||||||
cy.contains(".column.is-3-tablet", "One person going");
|
|
||||||
cy.get(".eventMetadataBlock")
|
|
||||||
.eq(1)
|
|
||||||
.contains("On Wednesday, September 15, 2021 from");
|
|
||||||
cy.contains(".column.is-3-tablet", "Public event");
|
|
||||||
|
|
||||||
cy.contains(".navbar-item", "My events").click();
|
|
||||||
cy.contains(".title", EVENT.title);
|
|
||||||
cy.contains(".content.column", "Organized by I'm a test user");
|
|
||||||
cy.contains(
|
|
||||||
".title-wrapper .date-component .datetime-container .month",
|
|
||||||
"Sep"
|
|
||||||
);
|
|
||||||
cy.contains(
|
|
||||||
".title-wrapper .date-component .datetime-container .day",
|
|
||||||
"15"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,124 +0,0 @@
|
|||||||
import onBeforeLoad from "./browser-language";
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.clearLocalStorage();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Login", () => {
|
|
||||||
it("Tests that everything is present", () => {
|
|
||||||
cy.visit("/login", { onBeforeLoad });
|
|
||||||
|
|
||||||
cy.get("form .field").first().contains("label", "Email");
|
|
||||||
cy.get("form .field").last().contains("label", "Password");
|
|
||||||
cy.get("form").contains("button.button", "Login");
|
|
||||||
cy.get("form")
|
|
||||||
.contains(".control a.button", "Forgot your password ?")
|
|
||||||
.click();
|
|
||||||
cy.url().should("include", "/password-reset/send");
|
|
||||||
cy.go("back");
|
|
||||||
|
|
||||||
cy.get("form").contains(".control a.button", "Register").click();
|
|
||||||
cy.url().should("include", "/register/user");
|
|
||||||
|
|
||||||
cy.go("back");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to login with incorrect credentials", () => {
|
|
||||||
cy.visit("/login", { onBeforeLoad });
|
|
||||||
cy.get("input[type=email]")
|
|
||||||
.type("notanemail")
|
|
||||||
.should("have.value", "notanemail");
|
|
||||||
cy.get("input[type=password]").click();
|
|
||||||
cy.contains("button.button.is-primary.is-large", "Login").click();
|
|
||||||
// cy.get('form .field').first().contains('p.help.is-danger', '@');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to login with invalid credentials", () => {
|
|
||||||
cy.visit("/login", { onBeforeLoad });
|
|
||||||
cy.get("input[type=email]")
|
|
||||||
.type("test@email.com")
|
|
||||||
.should("have.value", "test@email.com");
|
|
||||||
cy.get("input[type=password]")
|
|
||||||
.type("badPassword")
|
|
||||||
.should("have.value", "badPassword");
|
|
||||||
cy.contains("button.button.is-primary.is-large", "Login").click();
|
|
||||||
|
|
||||||
cy.contains(
|
|
||||||
".message.is-danger",
|
|
||||||
"No user account with this email was found. Maybe you made a typo?"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to login with valid credentials", () => {
|
|
||||||
cy.visit("/login", { onBeforeLoad });
|
|
||||||
cy.get("input[type=email]").type("user@email.com");
|
|
||||||
cy.get("input[type=password]").type("some password");
|
|
||||||
cy.get("form").submit();
|
|
||||||
cy.get(".navbar-end .navbar-link span.icon i").should(
|
|
||||||
"have.class",
|
|
||||||
"mdi-account-circle"
|
|
||||||
);
|
|
||||||
cy.contains("article.message.is-info", "Welcome back I'm a test user");
|
|
||||||
cy.get(".navbar-item.has-dropdown").click();
|
|
||||||
cy.get(".navbar-item").last().contains("Log out").click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to login with valid credentials but unconfirmed account", () => {
|
|
||||||
cy.visit("/login", { onBeforeLoad });
|
|
||||||
cy.get("input[type=email]").type("unconfirmed@email.com");
|
|
||||||
cy.get("input[type=password]").type("some password");
|
|
||||||
cy.get("form").submit();
|
|
||||||
cy.contains(
|
|
||||||
".message.is-danger",
|
|
||||||
"The user account you're trying to login as has not been confirmed yet. Check your email inbox and eventually your spam folder.You may also ask to resend confirmation email."
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tries to login with valid credentials, confirmed account but no profile", () => {
|
|
||||||
cy.visit("/login", { onBeforeLoad });
|
|
||||||
cy.get("input[type=email]").type("confirmed@email.com");
|
|
||||||
cy.get("input[type=password]").type("some password");
|
|
||||||
cy.get("form").submit();
|
|
||||||
|
|
||||||
cy.contains(
|
|
||||||
".message",
|
|
||||||
"To achieve your registration, please create a first identity profile."
|
|
||||||
);
|
|
||||||
cy.get("form > .field")
|
|
||||||
.eq(1)
|
|
||||||
.contains("label", "Username")
|
|
||||||
.parent()
|
|
||||||
.find("input")
|
|
||||||
.type("test_user");
|
|
||||||
cy.get("form > .field")
|
|
||||||
.first()
|
|
||||||
.contains("label", "Display name")
|
|
||||||
.parent()
|
|
||||||
.find("input")
|
|
||||||
.type("Duplicate");
|
|
||||||
cy.get("form > .field")
|
|
||||||
.eq(2)
|
|
||||||
.contains("label", "Description")
|
|
||||||
.parent()
|
|
||||||
.find("textarea")
|
|
||||||
.type("This shouln't work because it' using a dupublicated username");
|
|
||||||
cy.get(".control.has-text-centered")
|
|
||||||
.contains("button", "Create my profile")
|
|
||||||
.click();
|
|
||||||
cy.contains(".help.is-danger", "This username is already taken.");
|
|
||||||
|
|
||||||
cy.get("form .field input").first(0).clear().type("test_user_2");
|
|
||||||
cy.get("form .field input").eq(1).type("Not");
|
|
||||||
cy.get("form .field textarea").clear().type("This will now work");
|
|
||||||
cy.get("form").submit();
|
|
||||||
|
|
||||||
cy.get(".navbar-link span.icon i").should(
|
|
||||||
"have.class",
|
|
||||||
"mdi-account-circle"
|
|
||||||
);
|
|
||||||
cy.contains(
|
|
||||||
"article.message.is-info",
|
|
||||||
"Welcome to Mobilizon, test_user_2!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,92 +0,0 @@
|
|||||||
import onBeforeLoad from "./browser-language";
|
|
||||||
|
|
||||||
describe("Registration", () => {
|
|
||||||
it("Tests that everything is present", () => {
|
|
||||||
cy.visit("/register/user", { onBeforeLoad });
|
|
||||||
|
|
||||||
cy.get("form .field").first().contains("label", "Email");
|
|
||||||
cy.get("form .field").eq(1).contains("label", "Password");
|
|
||||||
|
|
||||||
cy.get("input[type=email]").click();
|
|
||||||
cy.get("input[type=password]").type("short").should("have.value", "short");
|
|
||||||
cy.get("form").contains("button.button.is-primary", "Register");
|
|
||||||
|
|
||||||
cy.get("form")
|
|
||||||
.contains(".control a.button", "Didn't receive the instructions ?")
|
|
||||||
.click();
|
|
||||||
cy.url().should("include", "/resend-instructions");
|
|
||||||
cy.go("back");
|
|
||||||
|
|
||||||
cy.get("form")
|
|
||||||
.get(".control a.button")
|
|
||||||
.contains("Login")
|
|
||||||
.click({ force: true });
|
|
||||||
cy.url().should("include", "/login");
|
|
||||||
|
|
||||||
cy.go("back");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Tests that registration works", () => {
|
|
||||||
cy.visit("/register/user", { onBeforeLoad });
|
|
||||||
cy.get("input[type=email]").type("user2register@email.com");
|
|
||||||
cy.get("input[type=password]").type("userPassword");
|
|
||||||
cy.get("form").contains("button.button.is-primary", "Register").click();
|
|
||||||
|
|
||||||
cy.url().should("include", "/register/profile");
|
|
||||||
cy.get("form > .field")
|
|
||||||
.eq(1)
|
|
||||||
.contains("label", "Username")
|
|
||||||
.parent()
|
|
||||||
.find("input")
|
|
||||||
.type("tester");
|
|
||||||
cy.get("form > .field")
|
|
||||||
.first()
|
|
||||||
.contains("label", "Display name")
|
|
||||||
.parent()
|
|
||||||
.find("input")
|
|
||||||
.type("tester account");
|
|
||||||
cy.get("form > .field")
|
|
||||||
.eq(2)
|
|
||||||
.contains("label", "Description")
|
|
||||||
.parent()
|
|
||||||
.find("textarea")
|
|
||||||
.type("This is a test account");
|
|
||||||
cy.get(".control.has-text-centered")
|
|
||||||
.contains("button", "Create my profile")
|
|
||||||
.click();
|
|
||||||
|
|
||||||
cy.contains(
|
|
||||||
"article.message.is-success",
|
|
||||||
"Your account is nearly ready, tester"
|
|
||||||
).contains("A validation email was sent to user2register@email.com");
|
|
||||||
|
|
||||||
cy.visit("/sent_emails");
|
|
||||||
|
|
||||||
cy.get("iframe")
|
|
||||||
.first()
|
|
||||||
.iframeLoaded()
|
|
||||||
.its("document")
|
|
||||||
.getInDocument("a")
|
|
||||||
.eq(1)
|
|
||||||
.contains("Activate my account")
|
|
||||||
.invoke("attr", "href")
|
|
||||||
.then((href) => {
|
|
||||||
cy.visit(href);
|
|
||||||
});
|
|
||||||
|
|
||||||
// cy.url().should('include', '/validate/');
|
|
||||||
// cy.contains('Your account is being validated');
|
|
||||||
cy.location().should((loc) => {
|
|
||||||
expect(loc.pathname).to.eq("/");
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(".navbar-link span.icon i").should(
|
|
||||||
"have.class",
|
|
||||||
"mdi-account-circle"
|
|
||||||
);
|
|
||||||
cy.contains(
|
|
||||||
"article.message.is-info",
|
|
||||||
"Welcome to Mobilizon, tester account!"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,159 +0,0 @@
|
|||||||
// ***********************************************
|
|
||||||
// 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 is will overwrite an existing command --
|
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
||||||
|
|
||||||
const AUTH_ACCESS_TOKEN = "auth-access-token";
|
|
||||||
const AUTH_REFRESH_TOKEN = "auth-refresh-token";
|
|
||||||
const AUTH_USER_ID = "auth-user-id";
|
|
||||||
const AUTH_USER_EMAIL = "auth-user-email";
|
|
||||||
const AUTH_USER_ACTOR_ID = "auth-user-actor-id";
|
|
||||||
const AUTH_USER_ROLE = "auth-user-role";
|
|
||||||
|
|
||||||
const LOCAL_STORAGE_MEMORY = {};
|
|
||||||
|
|
||||||
Cypress.Commands.add("saveLocalStorage", () => {
|
|
||||||
Object.keys(localStorage).forEach((key) => {
|
|
||||||
LOCAL_STORAGE_MEMORY[key] = localStorage[key];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add("restoreLocalStorage", () => {
|
|
||||||
Object.keys(LOCAL_STORAGE_MEMORY).forEach((key) => {
|
|
||||||
localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add("clearLocalStorage", () => {
|
|
||||||
Object.keys(LOCAL_STORAGE_MEMORY).forEach((key) => {
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add("loginUser", () => {
|
|
||||||
console.log("Going to login an user");
|
|
||||||
const loginMutation = `
|
|
||||||
mutation Login($email: String!, $password: String!) {
|
|
||||||
login(email: $email, password: $password) {
|
|
||||||
accessToken,
|
|
||||||
refreshToken,
|
|
||||||
user {
|
|
||||||
id,
|
|
||||||
email,
|
|
||||||
role
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}`;
|
|
||||||
|
|
||||||
const body = JSON.stringify({
|
|
||||||
operationName: "Login",
|
|
||||||
query: loginMutation,
|
|
||||||
variables: { email: "user@email.com", password: "some password" },
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.request({
|
|
||||||
url: "http://localhost:4000/api",
|
|
||||||
body,
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
}).then((res) => {
|
|
||||||
console.log("Reply from server when logging-in", res);
|
|
||||||
const obj = res.body.data.login;
|
|
||||||
console.log("Login data: ", obj);
|
|
||||||
|
|
||||||
localStorage.setItem(AUTH_USER_ID, `${obj.user.id}`);
|
|
||||||
localStorage.setItem(AUTH_USER_EMAIL, obj.user.email);
|
|
||||||
localStorage.setItem(AUTH_USER_ROLE, obj.user.role);
|
|
||||||
|
|
||||||
localStorage.setItem(AUTH_USER_ACTOR_ID, `${obj.id}`);
|
|
||||||
localStorage.setItem(AUTH_ACCESS_TOKEN, obj.accessToken);
|
|
||||||
localStorage.setItem(AUTH_REFRESH_TOKEN, obj.refreshToken);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// const increaseFetches = () => {
|
|
||||||
// const count = Cypress.env('fetchCount') || 0;
|
|
||||||
// Cypress.env('fetchCount', count + 1);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const decreaseFetches = () => {
|
|
||||||
const count = Cypress.env("fetchCount") || 0;
|
|
||||||
Cypress.env("fetchCount", count - 1);
|
|
||||||
};
|
|
||||||
|
|
||||||
const buildTrackableFetchWithSessionId =
|
|
||||||
(fetch) => (fetchUrl, fetchOptions) => {
|
|
||||||
const { headers } = fetchOptions;
|
|
||||||
const modifiedHeaders = {
|
|
||||||
"x-session-id": Cypress.env("sessionId"),
|
|
||||||
...headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
const modifiedOptions = { ...fetchOptions, headers: modifiedHeaders };
|
|
||||||
|
|
||||||
return fetch(fetchUrl, modifiedOptions)
|
|
||||||
.then((result) => {
|
|
||||||
decreaseFetches();
|
|
||||||
return Promise.resolve(result);
|
|
||||||
})
|
|
||||||
.catch((result) => {
|
|
||||||
decreaseFetches();
|
|
||||||
return Promise.reject(result);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
Cypress.on("window:before:load", (win) => {
|
|
||||||
cy.stub(win, "fetch", buildTrackableFetchWithSessionId(fetch));
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add("waitForFetches", () => {
|
|
||||||
if (Cypress.env("fetchCount") <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cy.waitForFetches();
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add("iframeLoaded", { prevSubject: "element" }, ($iframe) => {
|
|
||||||
const contentWindow = $iframe.prop("contentWindow");
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (contentWindow && contentWindow.document.readyState === "complete") {
|
|
||||||
resolve(contentWindow);
|
|
||||||
} else {
|
|
||||||
$iframe.on("load", () => {
|
|
||||||
resolve(contentWindow);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Cypress.Commands.add(
|
|
||||||
"getInDocument",
|
|
||||||
{ prevSubject: "document" },
|
|
||||||
(document, selector) => Cypress.$(selector, document)
|
|
||||||
);
|
|
@ -1,20 +0,0 @@
|
|||||||
// ***********************************************************
|
|
||||||
// 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";
|
|
||||||
|
|
||||||
// Alternatively you can use CommonJS syntax:
|
|
||||||
// require('./commands')
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"allowJs": true,
|
|
||||||
"baseUrl": "../node_modules",
|
|
||||||
"types": ["cypress"]
|
|
||||||
},
|
|
||||||
"include": ["**/*.*"]
|
|
||||||
}
|
|
1916
js/yarn.lock
1916
js/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -56,7 +56,7 @@ defmodule Mobilizon.Federation.ActivityPub.Utils do
|
|||||||
%{
|
%{
|
||||||
"@context" => [
|
"@context" => [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"https://litepub.social/context.jsonld",
|
"https://w3id.org/security/v1",
|
||||||
%{
|
%{
|
||||||
"@language" => "und",
|
"@language" => "und",
|
||||||
"sc" => "http://schema.org#",
|
"sc" => "http://schema.org#",
|
||||||
|
Loading…
Reference in New Issue
Block a user