diff options
-rw-r--r-- | .drone.jsonnet | 144 | ||||
-rw-r--r-- | .drone.yml | 2096 | ||||
-rw-r--r-- | package-lock.json | 164 | ||||
-rw-r--r-- | package.json | 2 | ||||
-rw-r--r-- | src/components/Description/Description.vue | 41 | ||||
-rw-r--r-- | src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue | 7 | ||||
-rw-r--r-- | src/components/MessagesList/MessagesGroup/Message/Message.spec.js | 479 | ||||
-rw-r--r-- | src/components/MessagesList/MessagesGroup/Message/Message.vue | 387 | ||||
-rw-r--r-- | src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js | 437 | ||||
-rw-r--r-- | src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue | 453 | ||||
-rw-r--r-- | src/components/Quote.vue | 3 | ||||
-rw-r--r-- | src/services/messagesService.js | 21 | ||||
-rw-r--r-- | src/store/messagesStore.js | 101 | ||||
-rw-r--r-- | src/store/messagesStore.spec.js | 7 | ||||
-rw-r--r-- | src/store/reactionsStore.js | 103 | ||||
-rw-r--r-- | src/store/storeConfig.js | 2 |
16 files changed, 2429 insertions, 2018 deletions
diff --git a/.drone.jsonnet b/.drone.jsonnet new file mode 100644 index 000000000..ee60dc208 --- /dev/null +++ b/.drone.jsonnet @@ -0,0 +1,144 @@ +## 1. Download/install drone binary: +## curl -L https://github.com/harness/drone-cli/releases/latest/download/drone_linux_amd64.tar.gz | tar zx +## 2. Adjust the matrix as wished +## 3. Run: ./drone jsonnet --stream --format yml +## 4. Commit the result + +local Pipeline(test_set, database, services) = { + kind: "pipeline", + name: "int-"+database+"-"+test_set, + services: services, + steps: [ + { + name: "integration-"+test_set, + image: "ghcr.io/nextcloud/continuous-integration-php8.0:latest", + environment: { + APP_NAME: "spreed", + CORE_BRANCH: "master", + GUESTS_BRANCH: "master", + DATABASEHOST: database + }, + commands: [ + "bash tests/drone-run-integration-tests.sh || exit 0", + "wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh", + "bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST", + "cd ../server", + "./occ app:enable $APP_NAME", + ] + ( + if test_set == "conversation" || test_set == "conversation-2" then [ + "git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests" + ] else [] + ) + [ + "cd apps/$APP_NAME", + "cd tests/integration/", + "bash run.sh features/"+test_set + ] + } + ], + trigger: { + branch: [ + "master", + "stable*" + ], + event: ( + if database == "sqlite" then ["pull_request", "push"] else ["push"] + ) + } +}; + +local PipelineSQLite(test_set) = Pipeline( + test_set, + "sqlite", + [ + { + name: "cache", + image: "ghcr.io/nextcloud/continuous-integration-redis:latest" + } + ] +); + +local PipelineMySQL(test_set) = Pipeline( + test_set, + "mysql", + [ + { + name: "cache", + image: "ghcr.io/nextcloud/continuous-integration-redis:latest" + }, + { + name: "mysql", + image: "ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4", + environment: { + MYSQL_ROOT_PASSWORD: "owncloud", + MYSQL_USER: "oc_autotest", + MYSQL_PASSWORD: "owncloud", + MYSQL_DATABASE: "oc_autotest" + }, + command: [ + "--innodb_large_prefix=true", + "--innodb_file_format=barracuda", + "--innodb_file_per_table=true" + ], + tmpfs: [ + "/var/lib/mysql" + ] + } + ] +); + +local PipelinePostgreSQL(test_set) = Pipeline( + test_set, + "pgsql", + [ + { + name: "cache", + image: "ghcr.io/nextcloud/continuous-integration-redis:latest" + }, + { + name: "pgsql", + image: "ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13", + environment: { + POSTGRES_USER: "oc_autotest", + POSTGRES_DB: "oc_autotest_dummy", + POSTGRES_HOST_AUTH_METHOD: "trust", + POSTGRES_PASSWORD: "" + }, + tmpfs: [ + "/var/lib/postgresql/data" + ] + } + ] +); + + +[ + PipelineSQLite("callapi"), + PipelineSQLite("chat"), + PipelineSQLite("command"), + PipelineSQLite("conversation"), + PipelineSQLite("conversation-2"), + PipelineSQLite("federation"), + PipelineSQLite("reaction"), + PipelineSQLite("sharing"), + PipelineSQLite("sharing-2"), + + PipelineMySQL("callapi"), + PipelineMySQL("chat"), + PipelineMySQL("command"), + PipelineMySQL("conversation"), + PipelineMySQL("conversation-2"), + PipelineMySQL("federation"), + PipelineMySQL("reaction"), + PipelineMySQL("sharing"), + PipelineMySQL("sharing-2"), + + PipelinePostgreSQL("callapi"), + PipelinePostgreSQL("chat"), + PipelinePostgreSQL("command"), + PipelinePostgreSQL("conversation"), + PipelinePostgreSQL("conversation-2"), + PipelinePostgreSQL("federation"), + PipelinePostgreSQL("reaction"), + PipelinePostgreSQL("sharing"), + PipelinePostgreSQL("sharing-2"), +] diff --git a/.drone.yml b/.drone.yml index 1d07dc769..0fb8eeedf 100644 --- a/.drone.yml +++ b/.drone.yml @@ -1,1464 +1,996 @@ +--- kind: pipeline name: int-sqlite-callapi - -steps: - - name: integration-callapi - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/callapi - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/callapi + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-callapi trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-sqlite-chat - -steps: - - name: integration-chat - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/chat - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/chat + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-chat trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-sqlite-command - -steps: - - name: integration-command - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/command - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/command + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-command trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-sqlite-conversation - -steps: - - name: integration-conversation - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/conversation - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/conversation + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-conversation trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-sqlite-conversation-2 - -steps: - - name: integration-conversation-2 - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/conversation-2 - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/conversation-2 + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-conversation-2 trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline -name: int-sqlite-reaction - -steps: - - name: integration-reaction - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/reaction - +name: int-sqlite-federation services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/federation + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-federation trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline -name: int-sqlite-federation - -steps: - - name: integration-federation - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/federation - +name: int-sqlite-reaction services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/reaction + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-reaction trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-sqlite-sharing - -steps: - - name: integration-sharing - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/sharing - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/sharing + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-sharing trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-sqlite-sharing-2 - -steps: - - name: integration-sharing-2 - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: sqlite - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/sharing-2 - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/sharing-2 + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: sqlite + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-sharing-2 trigger: branch: - - master - - stable* + - master + - stable* event: - - pull_request - - push - + - pull_request + - push --- kind: pipeline name: int-mysql-callapi - -steps: - - name: integration-callapi - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/callapi - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/callapi + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-callapi trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-mysql-chat - -steps: - - name: integration-chat - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/chat - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/chat + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-chat trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-mysql-command - -steps: - - name: integration-command - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/command - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/command + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-command trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-mysql-conversation - -steps: - - name: integration-conversation - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/conversation - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/conversation + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-conversation trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-mysql-conversation-2 - -steps: - - name: integration-conversation-2 - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/conversation-2 - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/conversation-2 + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-conversation-2 trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline -name: int-mysql-reaction - -steps: - - name: integration-reaction - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/reaction - +name: int-mysql-federation services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/federation + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-federation trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline -name: int-mysql-federation - -steps: - - name: integration-federation - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/federation - +name: int-mysql-reaction services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/reaction + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-reaction trigger: branch: - - master - - stable* + - master + - stable* event: - # - pull_request - - push - + - push --- kind: pipeline name: int-mysql-sharing - -steps: - - name: integration-sharing - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/sharing - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/sharing + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-sharing trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-mysql-sharing-2 - -steps: - - name: integration-sharing-2 - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: mysql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/sharing-2 - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: mysql - image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 - environment: - MYSQL_ROOT_PASSWORD: owncloud - MYSQL_USER: oc_autotest - MYSQL_PASSWORD: owncloud - MYSQL_DATABASE: oc_autotest - command: [ "--innodb_large_prefix=true", "--innodb_file_format=barracuda", "--innodb_file_per_table=true" ] - tmpfs: - - /var/lib/mysql - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- command: + - --innodb_large_prefix=true + - --innodb_file_format=barracuda + - --innodb_file_per_table=true + environment: + MYSQL_DATABASE: oc_autotest + MYSQL_PASSWORD: owncloud + MYSQL_ROOT_PASSWORD: owncloud + MYSQL_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-mariadb-10.4:10.4 + name: mysql + tmpfs: + - /var/lib/mysql +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/sharing-2 + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: mysql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-sharing-2 trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-callapi - -steps: - - name: integration-callapi - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/callapi - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/callapi + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-callapi trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-chat - -steps: - - name: integration-chat - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/chat - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/chat + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-chat trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-command - -steps: - - name: integration-command - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/command - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/command + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-command trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-conversation - -steps: - - name: integration-conversation - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/conversation - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/conversation + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-conversation trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-conversation-2 - -steps: - - name: integration-conversation-2 - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/conversation-2 - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - git clone --depth 1 -b $GUESTS_BRANCH https://github.com/nextcloud/guests apps/guests + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/conversation-2 + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-conversation-2 trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline -name: int-pgsql-reaction - -steps: - - name: integration-reaction - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - GUESTS_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - git clone --depth 1 -b "$GUESTS_BRANCH" https://github.com/nextcloud/guests apps/guests - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/reaction - +name: int-pgsql-federation services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/federation + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-federation trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline -name: int-pgsql-federation - -steps: - - name: integration-federation - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/federation - +name: int-pgsql-reaction services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/reaction + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-reaction trigger: branch: - - master - - stable* + - master + - stable* event: - # - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-sharing - -steps: - - name: integration-sharing - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/sharing - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/sharing + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-sharing trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - + - push --- kind: pipeline name: int-pgsql-sharing-2 - -steps: - - name: integration-sharing-2 - image: ghcr.io/nextcloud/continuous-integration-php8.0:latest - environment: - APP_NAME: spreed - CORE_BRANCH: master - DATABASEHOST: pgsql - commands: - - bash tests/drone-run-integration-tests.sh || exit 0 - - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh - - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST - - cd ../server - - ./occ app:enable $APP_NAME - - cd apps/$APP_NAME - - # Run integration tests - - cd tests/integration/ - - bash run.sh features/sharing-2 - services: - - name: cache - image: ghcr.io/nextcloud/continuous-integration-redis:latest - - name: pgsql - image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 - environment: - POSTGRES_USER: oc_autotest - POSTGRES_DB: oc_autotest_dummy - POSTGRES_HOST_AUTH_METHOD: trust - POSTGRES_PASSWORD: - tmpfs: - - /var/lib/postgresql/data - +- image: ghcr.io/nextcloud/continuous-integration-redis:latest + name: cache +- environment: + POSTGRES_DB: oc_autotest_dummy + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_PASSWORD: "" + POSTGRES_USER: oc_autotest + image: ghcr.io/nextcloud/continuous-integration-postgres-13:postgres-13 + name: pgsql + tmpfs: + - /var/lib/postgresql/data +steps: +- commands: + - bash tests/drone-run-integration-tests.sh || exit 0 + - wget https://raw.githubusercontent.com/nextcloud/travis_ci/master/before_install.sh + - bash ./before_install.sh $APP_NAME $CORE_BRANCH $DATABASEHOST + - cd ../server + - ./occ app:enable $APP_NAME + - cd apps/$APP_NAME + - cd tests/integration/ + - bash run.sh features/sharing-2 + environment: + APP_NAME: spreed + CORE_BRANCH: master + DATABASEHOST: pgsql + GUESTS_BRANCH: master + image: ghcr.io/nextcloud/continuous-integration-php8.0:latest + name: integration-sharing-2 trigger: branch: - - master - - stable* + - master + - stable* event: -# - pull_request - - push - -#--- -#kind: pipeline -#name: acc-sqlite-app-files -# -#steps: -# - name: acceptance-app-files -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: app-files -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-chat -# -#steps: -# - name: acceptance-chat -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: chat -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: cache -# image: ghcr.io/nextcloud/continuous-integration-redis:latest -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-conversation -# -#steps: -# - name: acceptance-conversation -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: conversation -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: cache -# image: ghcr.io/nextcloud/continuous-integration-redis:latest -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-conversation-public -# -#steps: -# - name: acceptance-conversation-public -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: conversation-public -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: cache -# image: ghcr.io/nextcloud/continuous-integration-redis:latest -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-lobby -# -#steps: -# - name: acceptance-lobby -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: lobby -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-public-share -# -#steps: -# - name: acceptance-public-share -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: public-share -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: cache -# image: ghcr.io/nextcloud/continuous-integration-redis:latest -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-public-share-auth -# -#steps: -# - name: acceptance-public-share-auth -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: public-share-auth -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: cache -# image: ghcr.io/nextcloud/continuous-integration-redis:latest -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push -# -#--- -#kind: pipeline -#name: acc-sqlite-room-shares -# -#steps: -# - name: acceptance-room-shares -# image: nextcloudci/acceptance-php7.1:acceptance-php7.1-2 -# environment: -# APP_NAME: spreed -# CORE_BRANCH: master -# SELENIUM_HOST: selenium -# TESTS_ACCEPTANCE: room-shares -# commands: -# - bash tests/drone-run-acceptance-tests.sh || exit 0 -# # Pre-setup steps -# - git clone --depth 1 -b $CORE_BRANCH https://github.com/nextcloud/server ../server -# - cp -R . ../server/apps/$APP_NAME -# - cd ../server -# - git submodule update --init -# - ln --symbolic `pwd` /var/www/html -# -# # Run acceptance tests -# - tests/acceptance/run-local.sh --acceptance-tests-dir apps/spreed/tests/acceptance --timeout-multiplier 10 --nextcloud-server-domain acceptance-$TESTS_ACCEPTANCE --selenium-server $SELENIUM_HOST:4444 allow-git-repository-modifications features/$TESTS_ACCEPTANCE.feature -# -#services: -# - name: cache -# image: ghcr.io/nextcloud/continuous-integration-redis:latest -# - name: selenium -# image: selenium/standalone-firefox:2.53.1-beryllium -# environment: -# # Reduce default log level for Selenium server (INFO) as it is too -# # verbose. -# JAVA_OPTS: -Dselenium.LOGGER.level=WARNING -# -#trigger: -# branch: -# - master -# - stable* -# event: -# - pull_request -# - push + - push diff --git a/package-lock.json b/package-lock.json index a268da93a..6fb93af03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,7 @@ "@nextcloud/stylelint-config": "^2.1.2", "@nextcloud/webpack-vue-config": "^5.0.0", "@vue/cli-plugin-unit-jest": "^4.5.15", - "@vue/cli-service": "^5.0.1", + "@vue/cli-service": "^5.0.3", "@vue/test-utils": "^1.3.0", "babel-loader-exclude-node-modules-except": "^1.2.1", "babel-plugin-add-module-exports": "^1.0.4", @@ -3859,27 +3859,27 @@ "dev": true }, "node_modules/@vue/cli-overlay": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.1.tgz", - "integrity": "sha512-8cLpHoQVYHoI4EjUG4+gbuUI9xxhkP5Vz/o/WLAkAAs//+1vE/A5AjCdLeQYGR9X6T4+b/kci2ArIpSSsRi8/Q==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.3.tgz", + "integrity": "sha512-LuEcVV8oOYcPnfoB3yBoE1fQ8p23ziFwV+BAXumXiDemIkg7piL5s+tqUgwV8fuOg3bileta+rtKQRNmcAoW6Q==", "dev": true }, "node_modules/@vue/cli-plugin-router": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.1.tgz", - "integrity": "sha512-Eu56AQUEK0uULgIQotZwi5eijYNBS3+znMc0u/kaI1puW3+f/qP3YCtffp5CeVcG2Kxwrx66XBI6PMHg8hPdmA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.3.tgz", + "integrity": "sha512-9GPewake2QhmnNe1p72Cejv0jyenH+gv3+3JlZf4kPR68ncqwF7YiUjxbnJnItWgq4Zep3+YqYmQ4PsIWjMPUQ==", "dev": true, "dependencies": { - "@vue/cli-shared-utils": "^5.0.1" + "@vue/cli-shared-utils": "^5.0.3" }, "peerDependencies": { "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" } }, "node_modules/@vue/cli-plugin-router/node_modules/@vue/cli-shared-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.1.tgz", - "integrity": "sha512-oL164c3yDhdLHgiFvSkXuP7z0eEY8gqTYzHHbvQJCIBtRZ/0H9Q7xICpAeMZ63lJvS2+fA5bQfv+kPII/kcjmQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.3.tgz", + "integrity": "sha512-xAHxFDYVohHWXOLdmGvka3ffQcRgKdACempFQkGJX74Q7OBf0zPf5WH+vQXhlR17eA3LmWdY+Nv8OfsIGim6Fg==", "dev": true, "dependencies": { "chalk": "^4.1.2", @@ -3888,7 +3888,7 @@ "launch-editor": "^2.2.1", "lru-cache": "^6.0.0", "node-fetch": "^2.6.7", - "node-ipc": "^9.1.1", + "node-ipc": "9.2.1", "open": "^8.0.2", "ora": "^5.3.0", "read-pkg": "^5.1.1", @@ -4179,28 +4179,28 @@ } }, "node_modules/@vue/cli-plugin-vuex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.1.tgz", - "integrity": "sha512-5J/n+Ht4r2eVuncwCXcZPHzYCz/2haktle4WcggWiKeg3jSQVUJbjviPBs6sOo3y/LG3CEfZMP9bPJjVDbexpQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.3.tgz", + "integrity": "sha512-QlrtXH+Z7YyER6r1Iz54yT6WoWyjr2rpFpIAcJP+KbXMvY8J97aupAkXYhw7Lg4zX4O2FhLKztZrjJT2Gx6ZSQ==", "dev": true, "peerDependencies": { "@vue/cli-service": "^3.0.0 || ^4.0.0 || ^5.0.0-0" } }, "node_modules/@vue/cli-service": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.1.tgz", - "integrity": "sha512-lhTW1d8waZv1VaRSvhE5pWWfkdmAgRTRSluDfyxkehZHMAWi//rd7a9zppN3k9Zr4X3oYVii+u7wR/RcTlr9cQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.3.tgz", + "integrity": "sha512-Mj41sc6VlPTwejZK4RTh17H3JLdJwKgeKHEgr17E4Vb6rZiByj29EYbr6lD9eEbbgCp/PwBkY4uPrMstCavcZQ==", "dev": true, "dependencies": { "@babel/helper-compilation-targets": "^7.12.16", "@soda/friendly-errors-webpack-plugin": "^1.8.0", "@soda/get-current-script": "^1.0.2", "@types/minimist": "^1.2.0", - "@vue/cli-overlay": "^5.0.1", - "@vue/cli-plugin-router": "^5.0.1", - "@vue/cli-plugin-vuex": "^5.0.1", - "@vue/cli-shared-utils": "^5.0.1", + "@vue/cli-overlay": "^5.0.3", + "@vue/cli-plugin-router": "^5.0.3", + "@vue/cli-plugin-vuex": "^5.0.3", + "@vue/cli-shared-utils": "^5.0.3", "@vue/component-compiler-utils": "^3.3.0", "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7", "@vue/web-component-wrapper": "^1.3.0", @@ -4287,9 +4287,9 @@ } }, "node_modules/@vue/cli-service/node_modules/@vue/cli-shared-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.1.tgz", - "integrity": "sha512-oL164c3yDhdLHgiFvSkXuP7z0eEY8gqTYzHHbvQJCIBtRZ/0H9Q7xICpAeMZ63lJvS2+fA5bQfv+kPII/kcjmQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.3.tgz", + "integrity": "sha512-xAHxFDYVohHWXOLdmGvka3ffQcRgKdACempFQkGJX74Q7OBf0zPf5WH+vQXhlR17eA3LmWdY+Nv8OfsIGim6Fg==", "dev": true, "dependencies": { "chalk": "^4.1.2", @@ -4298,7 +4298,7 @@ "launch-editor": "^2.2.1", "lru-cache": "^6.0.0", "node-fetch": "^2.6.7", - "node-ipc": "^9.1.1", + "node-ipc": "9.2.1", "open": "^8.0.2", "ora": "^5.3.0", "read-pkg": "^5.1.1", @@ -10098,9 +10098,9 @@ "dev": true }, "node_modules/easy-stack": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", - "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", "dev": true, "engines": { "node": ">=6.0.0" @@ -15175,21 +15175,21 @@ } }, "node_modules/js-message": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", - "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", "dev": true, "engines": { "node": ">=0.6.0" } }, "node_modules/js-queue": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", - "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", + "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", "dev": true, "dependencies": { - "easy-stack": "^1.0.0" + "easy-stack": "^1.0.1" }, "engines": { "node": ">=1.0.0" @@ -16620,17 +16620,17 @@ "dev": true }, "node_modules/node-ipc": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", - "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", + "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", "dev": true, "dependencies": { "event-pubsub": "4.3.0", - "js-message": "1.0.5", - "js-queue": "2.0.0" + "js-message": "1.0.7", + "js-queue": "2.0.2" }, "engines": { - "node": ">=4.0.0" + "node": ">=8.0.0" } }, "node_modules/node-modules-regexp": { @@ -27369,24 +27369,24 @@ "dev": true }, "@vue/cli-overlay": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.1.tgz", - "integrity": "sha512-8cLpHoQVYHoI4EjUG4+gbuUI9xxhkP5Vz/o/WLAkAAs//+1vE/A5AjCdLeQYGR9X6T4+b/kci2ArIpSSsRi8/Q==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-overlay/-/cli-overlay-5.0.3.tgz", + "integrity": "sha512-LuEcVV8oOYcPnfoB3yBoE1fQ8p23ziFwV+BAXumXiDemIkg7piL5s+tqUgwV8fuOg3bileta+rtKQRNmcAoW6Q==", "dev": true }, "@vue/cli-plugin-router": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.1.tgz", - "integrity": "sha512-Eu56AQUEK0uULgIQotZwi5eijYNBS3+znMc0u/kaI1puW3+f/qP3YCtffp5CeVcG2Kxwrx66XBI6PMHg8hPdmA==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-router/-/cli-plugin-router-5.0.3.tgz", + "integrity": "sha512-9GPewake2QhmnNe1p72Cejv0jyenH+gv3+3JlZf4kPR68ncqwF7YiUjxbnJnItWgq4Zep3+YqYmQ4PsIWjMPUQ==", "dev": true, "requires": { - "@vue/cli-shared-utils": "^5.0.1" + "@vue/cli-shared-utils": "^5.0.3" }, "dependencies": { "@vue/cli-shared-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.1.tgz", - "integrity": "sha512-oL164c3yDhdLHgiFvSkXuP7z0eEY8gqTYzHHbvQJCIBtRZ/0H9Q7xICpAeMZ63lJvS2+fA5bQfv+kPII/kcjmQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.3.tgz", + "integrity": "sha512-xAHxFDYVohHWXOLdmGvka3ffQcRgKdACempFQkGJX74Q7OBf0zPf5WH+vQXhlR17eA3LmWdY+Nv8OfsIGim6Fg==", "dev": true, "requires": { "chalk": "^4.1.2", @@ -27395,7 +27395,7 @@ "launch-editor": "^2.2.1", "lru-cache": "^6.0.0", "node-fetch": "^2.6.7", - "node-ipc": "^9.1.1", + "node-ipc": "9.2.1", "open": "^8.0.2", "ora": "^5.3.0", "read-pkg": "^5.1.1", @@ -27610,26 +27610,26 @@ } }, "@vue/cli-plugin-vuex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.1.tgz", - "integrity": "sha512-5J/n+Ht4r2eVuncwCXcZPHzYCz/2haktle4WcggWiKeg3jSQVUJbjviPBs6sOo3y/LG3CEfZMP9bPJjVDbexpQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-plugin-vuex/-/cli-plugin-vuex-5.0.3.tgz", + "integrity": "sha512-QlrtXH+Z7YyER6r1Iz54yT6WoWyjr2rpFpIAcJP+KbXMvY8J97aupAkXYhw7Lg4zX4O2FhLKztZrjJT2Gx6ZSQ==", "dev": true, "requires": {} }, "@vue/cli-service": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.1.tgz", - "integrity": "sha512-lhTW1d8waZv1VaRSvhE5pWWfkdmAgRTRSluDfyxkehZHMAWi//rd7a9zppN3k9Zr4X3oYVii+u7wR/RcTlr9cQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-service/-/cli-service-5.0.3.tgz", + "integrity": "sha512-Mj41sc6VlPTwejZK4RTh17H3JLdJwKgeKHEgr17E4Vb6rZiByj29EYbr6lD9eEbbgCp/PwBkY4uPrMstCavcZQ==", "dev": true, "requires": { "@babel/helper-compilation-targets": "^7.12.16", "@soda/friendly-errors-webpack-plugin": "^1.8.0", "@soda/get-current-script": "^1.0.2", "@types/minimist": "^1.2.0", - "@vue/cli-overlay": "^5.0.1", - "@vue/cli-plugin-router": "^5.0.1", - "@vue/cli-plugin-vuex": "^5.0.1", - "@vue/cli-shared-utils": "^5.0.1", + "@vue/cli-overlay": "^5.0.3", + "@vue/cli-plugin-router": "^5.0.3", + "@vue/cli-plugin-vuex": "^5.0.3", + "@vue/cli-shared-utils": "^5.0.3", "@vue/component-compiler-utils": "^3.3.0", "@vue/vue-loader-v15": "npm:vue-loader@^15.9.7", "@vue/web-component-wrapper": "^1.3.0", @@ -27680,9 +27680,9 @@ }, "dependencies": { "@vue/cli-shared-utils": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.1.tgz", - "integrity": "sha512-oL164c3yDhdLHgiFvSkXuP7z0eEY8gqTYzHHbvQJCIBtRZ/0H9Q7xICpAeMZ63lJvS2+fA5bQfv+kPII/kcjmQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vue/cli-shared-utils/-/cli-shared-utils-5.0.3.tgz", + "integrity": "sha512-xAHxFDYVohHWXOLdmGvka3ffQcRgKdACempFQkGJX74Q7OBf0zPf5WH+vQXhlR17eA3LmWdY+Nv8OfsIGim6Fg==", "dev": true, "requires": { "chalk": "^4.1.2", @@ -27691,7 +27691,7 @@ "launch-editor": "^2.2.1", "lru-cache": "^6.0.0", "node-fetch": "^2.6.7", - "node-ipc": "^9.1.1", + "node-ipc": "9.2.1", "open": "^8.0.2", "ora": "^5.3.0", "read-pkg": "^5.1.1", @@ -32166,9 +32166,9 @@ "dev": true }, "easy-stack": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz", - "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", + "integrity": "sha512-wK2sCs4feiiJeFXn3zvY0p41mdU5VUgbgs1rNsc/y5ngFUijdWd+iIN8eoyuZHKB8xN6BL4PdWmzqFmxNg6V2w==", "dev": true }, "ecc-jsbn": { @@ -36153,18 +36153,18 @@ } }, "js-message": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz", - "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.7.tgz", + "integrity": "sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA==", "dev": true }, "js-queue": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz", - "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.2.tgz", + "integrity": "sha512-pbKLsbCfi7kriM3s1J4DDCo7jQkI58zPLHi0heXPzPlj0hjUsm+FesPUbE0DSbIVIK503A36aUBoCN7eMFedkA==", "dev": true, "requires": { - "easy-stack": "^1.0.0" + "easy-stack": "^1.0.1" } }, "js-tokens": { @@ -37368,14 +37368,14 @@ "dev": true }, "node-ipc": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.1.1.tgz", - "integrity": "sha512-FAyICv0sIRJxVp3GW5fzgaf9jwwRQxAKDJlmNFUL5hOy+W4X/I5AypyHoq0DXXbo9o/gt79gj++4cMr4jVWE/w==", + "version": "9.2.1", + "resolved": "https://registry.npmjs.org/node-ipc/-/node-ipc-9.2.1.tgz", + "integrity": "sha512-mJzaM6O3xHf9VT8BULvJSbdVbmHUKRNOH7zDDkCrA1/T+CVjq2WVIDfLt0azZRXpgArJtl3rtmEozrbXPZ9GaQ==", "dev": true, "requires": { "event-pubsub": "4.3.0", - "js-message": "1.0.5", - "js-queue": "2.0.0" + "js-message": "1.0.7", + "js-queue": "2.0.2" } }, "node-modules-regexp": { diff --git a/package.json b/package.json index 533ea9ebf..d20a5fa63 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "@nextcloud/stylelint-config": "^2.1.2", "@nextcloud/webpack-vue-config": "^5.0.0", "@vue/cli-plugin-unit-jest": "^4.5.15", - "@vue/cli-service": "^5.0.1", + "@vue/cli-service": "^5.0.3", "@vue/test-utils": "^1.3.0", "babel-loader-exclude-node-modules-except": "^1.2.1", "babel-plugin-add-module-exports": "^1.0.4", diff --git a/src/components/Description/Description.vue b/src/components/Description/Description.vue index 038b03e89..498b74803 100644 --- a/src/components/Description/Description.vue +++ b/src/components/Description/Description.vue @@ -35,21 +35,25 @@ @keydown.esc="handleCancelEditing" /> <template v-if="!loading"> <template v-if="editing"> - <button class="nc-button nc-button__main description__action" + <Button type="tertiary" :aria-label="t('spreed', 'Cancel editing description')" @click="handleCancelEditing"> - <Close decorative - title="" - :size="16" /> - </button> - <button class="nc-button nc-button__main primary description__action" + <template #icon> + <Close decorative + title="" + :size="20" /> + </template> + </Button> + <Button type="primary" :aria-label="t('spreed', 'Submit conversation description')" :disabled="!canSubmit" @click="handleSubmitDescription"> - <Check decorative - title="" - :size="16" /> - </button> + <template #icon> + <Check decorative + title="" + :size="20" /> + </template> + </Button> <div v-if="showCountDown" v-tooltip.auto="countDownWarningText" class="counter" @@ -58,14 +62,16 @@ <span>{{ charactersCountDown }}</span> </div> </template> - <button v-if="!editing && editable" - class="nc-button nc-button__main" + <Button v-if="!editing && editable" + type="tertiary" :aria-label="t('spreed', 'Edit conversation description')" @click="handleEditDescription"> - <Pencil decorative - title="" - :size="16" /> - </button> + <template #icon> + <Pencil decorative + title="" + :size="20" /> + </template> + </Button> </template> <div v-if="loading" class="icon-loading-small spinner" /> </div> @@ -77,6 +83,7 @@ import Check from 'vue-material-design-icons/Check' import Close from 'vue-material-design-icons/Close' import RichContentEditable from '@nextcloud/vue/dist/Components/RichContenteditable' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' +import Button from '@nextcloud/vue/dist/Components/Button' export default { name: 'Description', @@ -85,6 +92,7 @@ export default { Check, Close, RichContentEditable, + Button, }, directives: { @@ -281,7 +289,6 @@ export default { &__action { margin: 0 0 4px 4px; - } } diff --git a/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue b/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue index 7d29fedec..cc4c3bcca 100644 --- a/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue +++ b/src/components/LeftSidebar/NewGroupConversation/NewGroupConversation.vue @@ -87,6 +87,7 @@ <Button v-if="page===0" type="primary" :disabled="disabled" + class="navigation__button-right" @click="handleSetConversationName"> {{ t('spreed', 'Add participants') }} </Button> @@ -98,12 +99,14 @@ </Button> <Button v-if="page===1" type="primary" + class="navigation__button-right" @click="handleCreateConversation"> {{ t('spreed', 'Create conversation') }} </Button> <!-- Third page --> <Button v-if="page===2 && (error || isPublic)" type="primary" + class="navigation__button-right" @click="closeModal"> {{ t('spreed', 'Close') }} </Button> @@ -406,6 +409,10 @@ it back */ box-shadow: 0 -10px 5px var(--color-main-background); z-index: 1; width: 100%; + + &__button-right { + margin-left:auto; + } } .wrapper { diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.spec.js b/src/components/MessagesList/MessagesGroup/Message/Message.spec.js index 02017389f..608fe10a8 100644 --- a/src/components/MessagesList/MessagesGroup/Message/Message.spec.js +++ b/src/components/MessagesList/MessagesGroup/Message/Message.spec.js @@ -1,21 +1,21 @@ -import Vuex from 'vuex' -import { createLocalVue, shallowMount } from '@vue/test-utils' +import Vuex, { Store } from 'vuex' +import { createLocalVue, mount, shallowMount } from '@vue/test-utils' import { cloneDeep } from 'lodash' import { EventBus } from '../../../../services/EventBus' import storeConfig from '../../../../store/storeConfig' -import { CONVERSATION, PARTICIPANT, ATTENDEE } from '../../../../constants' +import { CONVERSATION, ATTENDEE } from '../../../../constants' import Check from 'vue-material-design-icons/Check' import CheckAll from 'vue-material-design-icons/CheckAll' import Quote from '../../../Quote' -import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' import Mention from './MessagePart/Mention' import FilePreview from './MessagePart/FilePreview' import DeckCard from './MessagePart/DeckCard' import Location from './MessagePart/Location' import DefaultParameter from './MessagePart/DefaultParameter' -import { findActionButton } from '../../../../test-helpers' +import MessageButtonsBar from './MessageButtonsBar/MessageButtonsBar.vue' import Message from './Message' +import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' // needed because of https://github.com/vuejs/vue-test-utils/issues/1507 const RichTextStub = { @@ -75,6 +75,20 @@ describe('Message.vue', () => { systemMessage: '', messageType: 'comment', } + + // Dummy message getter so that the message component is always + // properly mounted. + testStoreConfig.modules.messagesStore.getters.message + = jest.fn().mockReturnValue(() => { + return { + reactions: '', + } + }) + + // Dummy hasReactions getter so that the message component is always + // properly mounted. + testStoreConfig.modules.messagesStore.getters.hasReactions + = jest.fn().mockReturnValue(() => false) }) afterEach(() => { @@ -83,7 +97,7 @@ describe('Message.vue', () => { describe('message rendering', () => { beforeEach(() => { - store = new Vuex.Store(testStoreConfig) + store = new Store(testStoreConfig) }) test('renders rich text message', async () => { @@ -126,7 +140,7 @@ describe('Message.vue', () => { message: 'message two', }] }) - store = new Vuex.Store(testStoreConfig) + store = new Store(testStoreConfig) }) test('shows join call button on last message when a call is in progress', () => { @@ -246,12 +260,13 @@ describe('Message.vue', () => { messageParameters: {}, token: TOKEN, parentId: -1, + reactions: '', } messageProps.parent = 120 const messageGetterMock = jest.fn().mockReturnValue(parentMessage) testStoreConfig.modules.messagesStore.getters.message = jest.fn(() => messageGetterMock) - store = new Vuex.Store(testStoreConfig) + store = new Store(testStoreConfig) const wrapper = shallowMount(Message, { localVue, @@ -482,7 +497,7 @@ describe('Message.vue', () => { describe('author rendering', () => { const AUTHOR_SELECTOR = '.message-body__author' beforeEach(() => { - store = new Vuex.Store(testStoreConfig) + store = new Store(testStoreConfig) }) test('renders author if first message', async () => { @@ -511,10 +526,9 @@ describe('Message.vue', () => { }) describe('actions', () => { - const ACTIONS_SELECTOR = '.message__buttons-bar' beforeEach(() => { - store = new Vuex.Store(testStoreConfig) + store = new Store(testStoreConfig) }) test('does not render actions for system messages are available', async () => { @@ -528,8 +542,8 @@ describe('Message.vue', () => { await wrapper.vm.$nextTick() - const actionsEl = wrapper.find(ACTIONS_SELECTOR) - expect(actionsEl.exists()).toBe(false) + const messageButtonsBar = wrapper.findComponent(MessageButtonsBar) + expect(messageButtonsBar.exists()).toBe(false) }) test('does not render actions for temporary messages', async () => { @@ -543,13 +557,13 @@ describe('Message.vue', () => { await wrapper.vm.$nextTick() - const actionsEl = wrapper.find(ACTIONS_SELECTOR) - expect(actionsEl.exists()).toBe(false) + const messageButtonsBar = wrapper.findComponent(MessageButtonsBar) + expect(messageButtonsBar.exists()).toBe(false) }) test('actions become visible on mouse over', async () => { messageProps.sendingFailure = 'timeout' - const wrapper = shallowMount(Message, { + const wrapper = mount(Message, { localVue, store, propsData: messageProps, @@ -557,437 +571,76 @@ describe('Message.vue', () => { await wrapper.vm.$nextTick() - const actionsEl = wrapper.find(ACTIONS_SELECTOR) + const messageButtonsBar = wrapper.findComponent(MessageButtonsBar) - expect(wrapper.vm.showActions).toBe(false) - expect(actionsEl.isVisible()).toBe(false) + expect(wrapper.vm.showMessageButtonsBar).toBe(false) + expect(messageButtonsBar.isVisible()).toBe(false) await wrapper.find('.message-body').trigger('mouseover') - expect(wrapper.vm.showActions).toBe(true) - expect(actionsEl.isVisible()).toBe(true) + expect(wrapper.vm.showMessageButtonsBar).toBe(true) + expect(messageButtonsBar.isVisible()).toBe(true) await wrapper.find('.message-body').trigger('mouseleave') - expect(wrapper.vm.showActions).toBe(false) - expect(actionsEl.isVisible()).toBe(false) + expect(wrapper.vm.showMessageButtonsBar).toBe(false) + expect(messageButtonsBar.isVisible()).toBe(false) // actions are always present and rendered const actions = wrapper.findAllComponents({ name: 'Actions' }) expect(actions.length).toBe(2) }) + }) - describe('reply action', () => { - test('replies to message', async () => { - const replyAction = jest.fn() - testStoreConfig.modules.quoteReplyStore.actions.addMessageToBeReplied = replyAction - store = new Vuex.Store(testStoreConfig) - - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, - }, - propsData: messageProps, - }) - - await wrapper.find('.message-body').trigger('mouseover') - const actionButton = findActionButton(wrapper, 'Reply') - expect(actionButton.exists()).toBe(true) - expect(actionButton.isVisible()).toBe(true) - await actionButton.find('button').trigger('click') - - expect(replyAction).toHaveBeenCalledWith(expect.anything(), { - id: 123, - actorId: 'user-id-1', - actorType: 'users', - actorDisplayName: 'user-display-name-1', - message: 'test message', - messageParameters: {}, - messageType: 'comment', - systemMessage: '', - timestamp: new Date('2020-05-07 09:23:00').getTime() / 1000, - token: TOKEN, - }) - }) - - test('hides reply button when not replyable', async () => { - messageProps.isReplyable = false - store = new Vuex.Store(testStoreConfig) - - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, - }, - propsData: messageProps, - }) - - const actionButton = findActionButton(wrapper, 'Reply') - expect(actionButton.isVisible()).toBe(false) - }) - }) - - describe('private reply action', () => { - test('creates a new conversation when replying to message privately', async () => { - const routerPushMock = jest.fn().mockResolvedValue() - const createOneToOneConversation = jest.fn() - testStoreConfig.modules.conversationsStore.actions.createOneToOneConversation = createOneToOneConversation - store = new Vuex.Store(testStoreConfig) - - messageProps.actorId = 'another-user' - - const wrapper = shallowMount(Message, { - localVue, - store, - mocks: { - $router: { - push: routerPushMock, - }, - }, - stubs: { - ActionButton, - }, - propsData: messageProps, - }) - - const actionButton = findActionButton(wrapper, 'Reply privately') - expect(actionButton.exists()).toBe(true) - - createOneToOneConversation.mockResolvedValueOnce({ - token: 'new-token', - }) - - await actionButton.find('button').trigger('click') - - expect(createOneToOneConversation).toHaveBeenCalledWith(expect.anything(), 'another-user') - - expect(routerPushMock).toHaveBeenCalledWith({ - name: 'conversation', - params: { - token: 'new-token', - }, - }) - }) - - /** - * @param {boolean} visible Whether or not the reply-private action is visible - */ - function testPrivateReplyActionVisible(visible) { - store = new Vuex.Store(testStoreConfig) - - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, - }, - propsData: messageProps, - }) - - const actionButton = findActionButton(wrapper, 'Reply privately') - expect(actionButton.exists()).toBe(visible) - } - - test('hides private reply action for own messages', async () => { - // using default message props which have the - // actor id set to the current user - testPrivateReplyActionVisible(false) - }) - - test('hides private reply action for one to one conversation type', async () => { - messageProps.actorId = 'another-user' - conversationProps.type = CONVERSATION.TYPE.ONE_TO_ONE - testPrivateReplyActionVisible(false) - }) - - test('hides private reply action for guest messages', async () => { - messageProps.actorId = 'guest-user' - messageProps.actorType = ATTENDEE.ACTOR_TYPE.GUESTS - testPrivateReplyActionVisible(false) - }) - - test('hides private reply action when current user is a guest', async () => { - messageProps.actorId = 'another-user' - getActorTypeMock.mockClear().mockReturnValue(() => ATTENDEE.ACTOR_TYPE.GUESTS) - testPrivateReplyActionVisible(false) - }) - }) - - describe('delete action', () => { - test('deletes message', async () => { - let resolveDeleteMessage - const deleteMessage = jest.fn().mockReturnValue(new Promise((resolve, reject) => { resolveDeleteMessage = resolve })) - testStoreConfig.modules.messagesStore.actions.deleteMessage = deleteMessage - store = new Vuex.Store(testStoreConfig) - - // need to mock the date to be within 6h - const mockDate = new Date('2020-05-07 10:00:00') - jest.spyOn(global.Date, 'now') - .mockImplementation(() => mockDate) - - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, - }, - propsData: messageProps, - }) - - const actionButton = findActionButton(wrapper, 'Delete') - expect(actionButton.exists()).toBe(true) - - await actionButton.find('button').trigger('click') - - expect(deleteMessage).toHaveBeenCalledWith(expect.anything(), { - message: { - token: TOKEN, - id: 123, - }, - placeholder: expect.anything(), - }) - - await wrapper.vm.$nextTick() - expect(wrapper.vm.isDeleting).toBe(true) - expect(wrapper.find('.icon-loading-small').exists()).toBe(true) - - resolveDeleteMessage(200) - // needs two updates... - await wrapper.vm.$nextTick() - await wrapper.vm.$nextTick() - - expect(wrapper.vm.isDeleting).toBe(false) - expect(wrapper.find('.icon-loading-small').exists()).toBe(false) - }) - - /** - * @param {boolean} visible Whether or not the delete action is visible - * @param {Date} mockDate The message date (deletion only works within 6h) - * @param {number} participantType The participant type of the user - */ - function testDeleteMessageVisible(visible, mockDate, participantType = PARTICIPANT.TYPE.USER) { - store = new Vuex.Store(testStoreConfig) - - // need to mock the date to be within 6h - if (!mockDate) { - mockDate = new Date('2020-05-07 10:00:00') - } - - jest.spyOn(global.Date, 'now') - .mockImplementation(() => mockDate) - - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, - }, - mixins: [{ - computed: { - participant: () => { - return { - actorId: 'user-id-1', - actorType: ATTENDEE.ACTOR_TYPE.USERS, - participantType, - } - }, - }, - }], - propsData: messageProps, - }) - - const actionButton = findActionButton(wrapper, 'Delete') - expect(actionButton.exists()).toBe(visible) - } - - test('hides delete action when message is older than 6 hours', () => { - testDeleteMessageVisible(false, new Date('2020-05-07 15:24:00')) - }) - - test('hides delete action when the conversation is read-only', () => { - conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY - testDeleteMessageVisible(false) - }) - - test('hides delete action for file messages', () => { - messageProps.message = '{file}' - messageProps.messageParameters.file = {} - testDeleteMessageVisible(false) - }) - - test('hides delete action on other people messages for non-moderators', () => { - messageProps.actorId = 'another-user' - conversationProps.type = CONVERSATION.TYPE.GROUP - testDeleteMessageVisible(false) - }) - - test('shows delete action on other people messages for moderators', () => { - messageProps.actorId = 'another-user' - conversationProps.type = CONVERSATION.TYPE.GROUP - testDeleteMessageVisible(true, null, PARTICIPANT.TYPE.MODERATOR) - }) - - test('shows delete action on other people messages for owner', () => { - messageProps.actorId = 'another-user' - conversationProps.type = CONVERSATION.TYPE.PUBLIC - testDeleteMessageVisible(true, null, PARTICIPANT.TYPE.OWNER) - }) - - test('does not show delete action even for guest moderators', () => { - messageProps.actorId = 'another-user' - conversationProps.type = CONVERSATION.TYPE.PUBLIC - testDeleteMessageVisible(false, null, PARTICIPANT.TYPE.GUEST_MODERATOR) - }) - - test('does not show delete action on other people messages in one to one conversations', () => { - messageProps.actorId = 'another-user' - conversationProps.type = CONVERSATION.TYPE.ONE_TO_ONE - testDeleteMessageVisible(false) - }) - }) - - test('marks message as unread', async () => { - const updateLastReadMessageAction = jest.fn().mockResolvedValueOnce() - const fetchConversationAction = jest.fn().mockResolvedValueOnce() - testStoreConfig.modules.conversationsStore.actions.updateLastReadMessage = updateLastReadMessageAction - testStoreConfig.modules.conversationsStore.actions.fetchConversation = fetchConversationAction - store = new Vuex.Store(testStoreConfig) - - messageProps.previousMessageId = 100 - - // appears even with more restrictive conditions - conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY - messageProps.actorId = 'another-user' - - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, - }, - mixins: [{ - computed: { - participant: () => { - return { - actorId: 'guest-id-1', - actorType: ATTENDEE.ACTOR_TYPE.GUESTS, - participantType: PARTICIPANT.TYPE.GUEST, - } - }, - }, - }], - propsData: messageProps, - }) - - const actionButton = findActionButton(wrapper, 'Mark as unread') - expect(actionButton.exists()).toBe(true) - - await actionButton.find('button').trigger('click') - // needs two updates... - await wrapper.vm.$nextTick() - await wrapper.vm.$nextTick() - - expect(updateLastReadMessageAction).toHaveBeenCalledWith(expect.anything(), { - token: TOKEN, - id: 100, - updateVisually: true, - }) - - expect(fetchConversationAction).toHaveBeenCalledWith(expect.anything(), { - token: TOKEN, - }) - }) - - test('copies message link', async () => { - const copyTextMock = jest.fn() + describe('delete action', () => { + test('deletes message', async () => { + let resolveDeleteMessage + const deleteMessage = jest.fn().mockReturnValue(new Promise((resolve, reject) => { resolveDeleteMessage = resolve })) + testStoreConfig.modules.messagesStore.actions.deleteMessage = deleteMessage + store = new Store(testStoreConfig) - // appears even with more restrictive conditions - conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY - messageProps.actorId = 'another-user' + // need to mock the date to be within 6h + const mockDate = new Date('2020-05-07 10:00:00') + jest.spyOn(global.Date, 'now') + .mockImplementation(() => mockDate) - const wrapper = shallowMount(Message, { + const wrapper = mount(Message, { localVue, store, - mocks: { - $copyText: copyTextMock, - }, stubs: { ActionButton, + MessageButtonsBar, }, - mixins: [{ - computed: { - participant: () => { - return { - actorId: 'guest-id-1', - actorType: ATTENDEE.ACTOR_TYPE.GUESTS, - participantType: PARTICIPANT.TYPE.GUEST, - } - }, - }, - }], propsData: messageProps, }) - const actionButton = findActionButton(wrapper, 'Copy message link') - expect(actionButton.exists()).toBe(true) - - await actionButton.find('button').trigger('click') + wrapper.find(MessageButtonsBar).vm.$emit('delete') - expect(copyTextMock).toHaveBeenCalledWith('http://localhost/nc-webroot/call/XXTOKENXX#message_123') - }) - - test('renders clickable custom actions', async () => { - const handler = jest.fn() - const handler2 = jest.fn() - const actionsGetterMock = jest.fn().mockReturnValue([{ - label: 'first action', - icon: 'some-icon', - callback: handler, - }, { - label: 'second action', - icon: 'some-icon2', - callback: handler2, - }]) - testStoreConfig.modules.messageActionsStore.getters.messageActions = actionsGetterMock - testStoreConfig.modules.messagesStore.getters.message = jest.fn(() => () => messageProps) - store = new Vuex.Store(testStoreConfig) - const wrapper = shallowMount(Message, { - localVue, - store, - stubs: { - ActionButton, + expect(deleteMessage).toHaveBeenCalledWith(expect.anything(), { + message: { + token: TOKEN, + id: 123, }, - propsData: messageProps, + placeholder: expect.anything(), }) - const actionButton = findActionButton(wrapper, 'first action') - expect(actionButton.exists()).toBe(true) - await actionButton.find('button').trigger('click') - - expect(handler).toHaveBeenCalledWith({ - apiVersion: 'v3', - message: messageProps, - metadata: conversationProps, - }) + await wrapper.vm.$nextTick() + expect(wrapper.vm.isDeleting).toBe(true) + expect(wrapper.find('.icon-loading-small').exists()).toBe(true) - const actionButton2 = findActionButton(wrapper, 'second action') - expect(actionButton2.exists()).toBe(true) - await actionButton2.find('button').trigger('click') + resolveDeleteMessage(200) + // needs two updates... + await wrapper.vm.$nextTick() + await wrapper.vm.$nextTick() - expect(handler2).toHaveBeenCalledWith({ - apiVersion: 'v3', - message: messageProps, - metadata: conversationProps, - }) + expect(wrapper.vm.isDeleting).toBe(false) + expect(wrapper.find('.icon-loading-small').exists()).toBe(false) }) }) describe('status', () => { beforeEach(() => { - store = new Vuex.Store(testStoreConfig) + store = new Store(testStoreConfig) }) test('lets user retry sending a timed out message', async () => { diff --git a/src/components/MessagesList/MessagesGroup/Message/Message.vue b/src/components/MessagesList/MessagesGroup/Message/Message.vue index 280b231e3..8cbe9650f 100644 --- a/src/components/MessagesList/MessagesGroup/Message/Message.vue +++ b/src/components/MessagesList/MessagesGroup/Message/Message.vue @@ -67,6 +67,7 @@ the main body of the message as well as a quote. class="date" :style="{'visibility': hasDate ? 'visible' : 'hidden'}" :class="{'date--self': showSentIcon}">{{ messageTime }}</span> + <!-- Message delivery status indicators --> <div v-if="sendingFailure" v-tooltip.auto="sendingErrorIconTooltip" @@ -78,13 +79,13 @@ the main body of the message as well as a quote. @focus="showReloadButton = true" @mouseleave="showReloadButton = true" @blur="showReloadButton = true"> - <button v-if="sendingErrorCanRetry && showReloadButton" + <Button v-if="sendingErrorCanRetry && showReloadButton" class="nc-button nc-button__main--dark" @click="handleRetry"> <Reload decorative title="" :size="16" /> - </button> + </Button> <AlertCircle v-else decorative title="" @@ -112,90 +113,58 @@ the main body of the message as well as a quote. </div> </div> </div> - <!-- Message Actions --> - <div v-if="hasActions" - v-show="showActions" - class="message__buttons-bar"> - <Actions v-show="isReplyable"> - <ActionButton icon="icon-reply" - @click.stop="handleReply"> - {{ t('spreed', 'Reply') }} - </ActionButton> - </Actions> - <Actions :force-menu="true" - :container="container" - :boundaries-element="containerElement"> - <ActionButton v-if="isPrivateReplyable" - icon="icon-user" - :close-after-click="true" - @click.stop="handlePrivateReply"> - {{ t('spreed', 'Reply privately') }} - </ActionButton> - <ActionButton icon="icon-external" - :close-after-click="true" - @click.stop.prevent="handleCopyMessageLink"> - {{ t('spreed', 'Copy message link') }} - </ActionButton> - <ActionButton :close-after-click="true" - @click.stop="handleMarkAsUnread"> - <template #icon> - <EyeOffOutline decorative - title="" - :size="16" /> - </template> - {{ t('spreed', 'Mark as unread') }} - </ActionButton> - <ActionLink v-if="linkToFile" - icon="icon-text" - :href="linkToFile"> - {{ t('spreed', 'Go to file') }} - </ActionLink> - <ActionButton v-if="!isCurrentGuest && !isFileShare" - :close-after-click="true" - @click.stop="showForwarder = true"> - <Share slot="icon" - :size="16" - decorative - title="" /> - {{ t('spreed', 'Forward message') }} - </ActionButton> - <ActionSeparator v-if="messageActions.length > 0" /> - <template v-for="action in messageActions"> - <ActionButton :key="action.label" - :icon="action.icon" - :close-after-click="true" - @click="action.callback(messageAPIData)"> - {{ action.label }} - </ActionButton> - </template> - <template v-if="isDeleteable"> - <ActionSeparator /> - <ActionButton icon="icon-delete" - :close-after-click="true" - @click.stop="handleDelete"> - {{ t('spreed', 'Delete') }} - </ActionButton> - </template> - </Actions> + + <!-- reactions buttons and popover with details --> + <div v-if="hasReactions" + class="message-body__reactions" + @mouseover="handleReactionsMouseOver"> + <Popover v-for="reaction in Object.keys(simpleReactions)" + :key="reaction" + :delay="200" + trigger="hover"> + <button v-if="simpleReactions[reaction]!== 0" + slot="trigger" + class="reaction-button" + @click="handleReactionClick(reaction)"> + <span class="reaction-button__emoji"> {{ reaction }} </span> + <span> {{ simpleReactions[reaction] }} </span> + </button> + <div v-if="detailedReactions" class="reaction-details"> + <p v-for="detailedReaction in detailedReactions[reaction]" :key="detailedReaction.actorDisplayName"> + {{ detailedReaction.actorDisplayName }} + </p> + </div> + </Popover> + + <!-- More reactions picker --> + <EmojiPicker :per-line="5" @select="addReactionToMessage"> + <button class="reaction-button"> + <EmoticonOutline :size="15" /> + </button> + </EmojiPicker> </div> </div> + + <!-- Message actions --> + <MessageButtonsBar v-if="hasMessageButtonsBar" + v-show="showMessageButtonsBar" + ref="messageButtonsBar" + :message-api-data="messageApiData" + :message-object="messageObject" + v-bind="$props" + :previous-message-id="previousMessageId" + :participant="participant" + @delete="handleDelete" /> <div v-if="isLastReadMessage" v-observe-visibility="lastReadMessageVisibilityChanged"> <div class="new-message-marker"> <span>{{ t('spreed', 'Unread messages') }}</span> </div> </div> - <Forwarder v-if="showForwarder" - :message-object="messageObject" - @close="showForwarder = false" /> </li> </template> <script> -import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' -import ActionLink from '@nextcloud/vue/dist/Components/ActionLink' -import Actions from '@nextcloud/vue/dist/Components/Actions' -import ActionSeparator from '@nextcloud/vue/dist/Components/ActionSeparator' import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip' import CallButton from '../../../TopBar/CallButton' import DeckCard from './MessagePart/DeckCard' @@ -206,26 +175,20 @@ import RichText from '@juliushaertl/vue-richtext' import AlertCircle from 'vue-material-design-icons/AlertCircle' import Check from 'vue-material-design-icons/Check' import CheckAll from 'vue-material-design-icons/CheckAll' -import EyeOffOutline from 'vue-material-design-icons/EyeOffOutline' import Reload from 'vue-material-design-icons/Reload' -import Share from 'vue-material-design-icons/Share' import Quote from '../../../Quote' import isInCall from '../../../../mixins/isInCall' import participant from '../../../../mixins/participant' import { EventBus } from '../../../../services/EventBus' import emojiRegex from 'emoji-regex' -import { PARTICIPANT, CONVERSATION, ATTENDEE } from '../../../../constants' import moment from '@nextcloud/moment' -import { - showError, - showSuccess, - showWarning, - TOAST_DEFAULT_TIMEOUT, -} from '@nextcloud/dialogs' -import { generateUrl } from '@nextcloud/router' import Location from './MessagePart/Location' import Contact from './MessagePart/Contact.vue' -import Forwarder from './MessagePart/Forwarder' +import MessageButtonsBar from './MessageButtonsBar/MessageButtonsBar.vue' +import EmojiPicker from '@nextcloud/vue/dist/Components/EmojiPicker' +import EmoticonOutline from 'vue-material-design-icons/EmoticonOutline.vue' +import Popover from '@nextcloud/vue/dist/Components/Popover' +import { showError, showSuccess, showWarning, TOAST_DEFAULT_TIMEOUT } from '@nextcloud/dialogs' export default { name: 'Message', @@ -235,20 +198,17 @@ export default { }, components: { - Actions, - ActionButton, - ActionLink, CallButton, Quote, RichText, AlertCircle, Check, CheckAll, - EyeOffOutline, Reload, - Share, - ActionSeparator, - Forwarder, + MessageButtonsBar, + EmojiPicker, + EmoticonOutline, + Popover, }, mixins: [ @@ -388,16 +348,15 @@ export default { data() { return { - showActions: false, + showMessageButtonsBar: false, // Is tall enough for both actions and date upon hovering isTallEnough: false, showReloadButton: false, isDeleting: false, // whether the message was seen, only used if this was marked as last read message seen: false, - // Shows/hides the message forwarder component - showForwarder: false, isActionMenuOpen: false, + detailedReactionsRequested: false, } }, @@ -418,10 +377,6 @@ export default { return this.$store.getters.message(this.token, this.id) }, - isConversationReadOnly() { - return this.conversation.readOnly === CONVERSATION.STATE.READ_ONLY - }, - isSystemMessage() { return this.systemMessage !== '' }, @@ -547,21 +502,13 @@ export default { return false } - return this.isSystemMessage || !this.showActions || this.isTallEnough + return this.isSystemMessage || !this.showMessageButtonsBar || this.isTallEnough }, - hasActions() { + hasMessageButtonsBar() { return !this.isSystemMessage && !this.isTemporary }, - container() { - return this.$store.getters.getMainContainerSelector() - }, - - containerElement() { - return document.querySelector(this.container) - }, - isTemporaryUpload() { return this.isTemporary && this.messageParameters.file }, @@ -595,55 +542,11 @@ export default { return t('spreed', 'You cannot send messages to this conversation at the moment') }, - isMyMsg() { - return this.actorId === this.$store.getters.getActorId() - && this.actorType === this.$store.getters.getActorType() - }, - - isFileShare() { - return this.message === '{file}' && this.messageParameters?.file - }, - - linkToFile() { - if (this.isFileShare) { - return this.messageParameters?.file?.link - } - return '' - }, - - isDeleteable() { - if (this.isConversationReadOnly) { - return false - } - - const isObjectShare = this.message === '{object}' - && this.messageParameters?.object - - return (moment(this.timestamp * 1000).add(6, 'h')) > moment() - && this.messageType === 'comment' - && !this.isDeleting - && !this.isFileShare - && !isObjectShare - && (this.isMyMsg - || (this.conversation.type !== CONVERSATION.TYPE.ONE_TO_ONE - && (this.participant.participantType === PARTICIPANT.TYPE.OWNER - || this.participant.participantType === PARTICIPANT.TYPE.MODERATOR))) - }, - - isPrivateReplyable() { - return this.isReplyable - && (this.conversation.type === CONVERSATION.TYPE.PUBLIC - || this.conversation.type === CONVERSATION.TYPE.GROUP) - && !this.isMyMsg - && this.actorType === ATTENDEE.ACTOR_TYPE.USERS - && this.$store.getters.getActorType() === ATTENDEE.ACTOR_TYPE.USERS - }, - messageActions() { return this.$store.getters.messageActions }, - messageAPIData() { + messageApiData() { return { message: this.messageObject, metadata: this.conversation, @@ -651,8 +554,16 @@ export default { } }, - isCurrentGuest() { - return this.$store.getters.getActorType() === 'guests' + hasReactions() { + return this.$store.getters.hasReactions(this.token, this.id) + }, + + simpleReactions() { + return this.messageObject.reactions + }, + + detailedReactions() { + return this.$store.getters.reactions(this.token, this.id) }, }, @@ -696,27 +607,74 @@ export default { // again another time this.$refs.message.classList.remove('highlight-animation') }, + handleRetry() { if (this.sendingErrorCanRetry) { EventBus.$emit('retry-message', this.id) EventBus.$emit('focus-chat-input') } }, - handleReply() { - this.$store.dispatch('addMessageToBeReplied', { - id: this.id, - actorId: this.actorId, - actorType: this.actorType, - actorDisplayName: this.actorDisplayName, - timestamp: this.timestamp, - systemMessage: this.systemMessage, - messageType: this.messageType, - message: this.message, - messageParameters: this.messageParameters, - token: this.token, - }) - EventBus.$emit('focus-chat-input') + + handleMouseover() { + this.showMessageButtonsBar = true + + }, + + handleReactionsMouseOver() { + if (this.hasReactions && !this.detailedReactionsRequested) { + this.getReactions() + } + }, + + handleMouseleave() { + if (!this.isActionMenuOpen) { + this.showMessageButtonsBar = false + } + }, + + async getReactions() { + try { + /** + * Get reaction details when the message is hovered for the first + * time. After that we rely on system messages to update the + * reactions. + */ + this.detailedReactionsRequested = true + await this.$store.dispatch('getReactions', { + token: this.token, + messageId: this.id, + }) + } catch { + this.detailedReactionsRequested = false + } + }, + + async handleReactionClick(clickedEmoji) { + if (!this.detailedReactionsRequested) { + await this.getReactions() + } + // Check if current user has already added this reaction to the message + const currentUserHasReacted = this.$store.getters.userHasReacted(this.actorId, this.token, this.id, clickedEmoji) + + if (!currentUserHasReacted) { + console.debug('adding reaction') + this.$store.dispatch('addReactionToMessage', { + token: this.token, + messageId: this.id, + selectedEmoji: clickedEmoji, + actorId: this.actorId, + }) + } else { + console.debug('user has already reacted, removing reaction') + this.$store.dispatch('removeReactionFromMessage', { + token: this.token, + messageId: this.id, + selectedEmoji: clickedEmoji, + actorId: this.actorId, + }) + } }, + async handleDelete() { this.isDeleting = true try { @@ -751,50 +709,19 @@ export default { this.isDeleting = false }, - handleMouseover() { - this.showActions = true - }, - - handleMouseleave() { - if (!this.isActionMenuOpen) { - this.showActions = false - } - }, - handleActionMenuUpdate(type) { - if (type === 'open') { - this.isActionMenuOpen = true - } else if (type === 'close') { - this.isActionMenuOpen = false - this.showActions = false - } - }, - async handlePrivateReply() { - // open the 1:1 conversation - const conversation = await this.$store.dispatch('createOneToOneConversation', this.actorId) - this.$router.push({ name: 'conversation', params: { token: conversation.token } }).catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`)) - }, - - async handleCopyMessageLink() { - try { - const link = window.location.protocol + '//' + window.location.host + generateUrl('/call/' + this.token) + '#message_' + this.id - await this.$copyText(link) - showSuccess(t('spreed', 'Message link copied to clipboard.')) - } catch (error) { - console.error('Error copying link: ', error) - showError(t('spreed', 'The link could not be copied.')) + addReactionToMessage(selectedEmoji) { + // Add reaction only if user hasn't reacted yet + if (!this.$store.getters.userHasReacted(this.actorId, this.token, this.messageObject.id, selectedEmoji)) { + this.$store.dispatch('addReactionToMessage', { + token: this.token, + messageId: this.messageObject.id, + selectedEmoji, + actorId: this.actorId, + }) + } else { + console.debug('Current user has already reacted') } - }, - - async handleMarkAsUnread() { - // update in backend + visually - await this.$store.dispatch('updateLastReadMessage', { - token: this.token, - id: this.previousMessageId, - updateVisually: true, - }) - // reload conversation to update additional attributes that have computed values - await this.$store.dispatch('fetchConversation', { token: this.token }) }, }, } @@ -811,6 +738,10 @@ export default { } } +.message { + position: relative; +} + .message-body { padding: 4px; font-size: $chat-font-size; @@ -874,6 +805,10 @@ export default { padding: 0 8px 0 8px; } } + &__reactions { + display: flex; + margin: 4px 0 4px -2px; + } } .date { @@ -939,18 +874,26 @@ export default { } } -.message__buttons-bar { - display: flex; - right: 14px; - bottom: -4px; - position: absolute; - z-index: 100000; +.reaction-button { + // Clear server rules + min-height: 0 !important; + padding: 0 8px !important; + font-weight: normal !important; + + margin: 0 2px; + height: 26px; background-color: var(--color-main-background); - border-radius: calc($clickable-area / 2); - box-shadow: 0 0 4px 0px var(--color-box-shadow); - & h6 { - margin-left: auto; + &__emoji { + margin: 0 4px 0 0; } + + &:hover { + background-color: var(--color-primary-element-lighter); + } +} + +.reaction-details { + padding: 8px; } </style> diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js new file mode 100644 index 000000000..78d91b6ec --- /dev/null +++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.spec.js @@ -0,0 +1,437 @@ +import Vuex, { Store } from 'vuex' +import { createLocalVue, shallowMount } from '@vue/test-utils' +import { cloneDeep } from 'lodash' +import storeConfig from '../../../../../store/storeConfig' +import { CONVERSATION, PARTICIPANT, ATTENDEE } from '../../../../../constants' +import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' +import { findActionButton } from '../../../../../test-helpers' +import MessageButtonsBar from './../MessageButtonsBar/MessageButtonsBar.vue' + +describe('MessageButtonsBar.vue', () => { + const TOKEN = 'XXTOKENXX' + let localVue + let testStoreConfig + let store + let messageProps + let conversationProps + let getActorTypeMock + + beforeEach(() => { + localVue = createLocalVue() + localVue.use(Vuex) + + conversationProps = { + token: TOKEN, + lastCommonReadMessage: 0, + type: CONVERSATION.TYPE.GROUP, + readOnly: CONVERSATION.STATE.READ_WRITE, + } + + testStoreConfig = cloneDeep(storeConfig) + testStoreConfig.modules.tokenStore.getters.getToken + = jest.fn().mockReturnValue(() => TOKEN) + testStoreConfig.modules.conversationsStore.getters.conversation + = jest.fn().mockReturnValue((token) => conversationProps) + testStoreConfig.modules.actorStore.getters.getActorId + = jest.fn().mockReturnValue(() => 'user-id-1') + getActorTypeMock = jest.fn().mockReturnValue(() => ATTENDEE.ACTOR_TYPE.USERS) + testStoreConfig.modules.actorStore.getters.getActorType = getActorTypeMock + + messageProps = { + message: 'test message', + actorType: ATTENDEE.ACTOR_TYPE.USERS, + actorId: 'user-id-1', + actorDisplayName: 'user-display-name-1', + messageParameters: {}, + id: 123, + isTemporary: false, + isFirstMessage: true, + isReplyable: true, + timestamp: new Date('2020-05-07 09:23:00').getTime() / 1000, + token: TOKEN, + systemMessage: '', + messageType: 'comment', + previousMessageId: 100, + messageObject: {}, + messageApiData: { + apiDummyData: 1, + }, + participant: { + actorId: 'user-id-1', + actorType: ATTENDEE.ACTOR_TYPE.USERS, + participantType: PARTICIPANT.TYPE.USER, + }, + } + }) + + afterEach(() => { + jest.clearAllMocks() + }) + + describe('actions', () => { + + beforeEach(() => { + store = new Store(testStoreConfig) + }) + + describe('reply action', () => { + test('replies to message', async () => { + const replyAction = jest.fn() + testStoreConfig.modules.quoteReplyStore.actions.addMessageToBeReplied = replyAction + store = new Store(testStoreConfig) + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Reply') + expect(actionButton.exists()).toBe(true) + expect(actionButton.isVisible()).toBe(true) + await actionButton.find('button').trigger('click') + + expect(replyAction).toHaveBeenCalledWith(expect.anything(), { + id: 123, + actorId: 'user-id-1', + actorType: 'users', + actorDisplayName: 'user-display-name-1', + message: 'test message', + messageParameters: {}, + messageType: 'comment', + systemMessage: '', + timestamp: new Date('2020-05-07 09:23:00').getTime() / 1000, + token: TOKEN, + previousMessageId: 100, + }) + }) + + test('hides reply button when not replyable', async () => { + messageProps.isReplyable = false + store = new Store(testStoreConfig) + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Reply') + expect(actionButton.isVisible()).toBe(false) + }) + }) + + describe('private reply action', () => { + test('creates a new conversation when replying to message privately', async () => { + const routerPushMock = jest.fn().mockResolvedValue() + const createOneToOneConversation = jest.fn() + testStoreConfig.modules.conversationsStore.actions.createOneToOneConversation = createOneToOneConversation + store = new Store(testStoreConfig) + + messageProps.actorId = 'another-user' + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + mocks: { + $router: { + push: routerPushMock, + }, + }, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Reply privately') + expect(actionButton.exists()).toBe(true) + + createOneToOneConversation.mockResolvedValueOnce({ + token: 'new-token', + }) + + await actionButton.find('button').trigger('click') + + expect(createOneToOneConversation).toHaveBeenCalledWith(expect.anything(), 'another-user') + + expect(routerPushMock).toHaveBeenCalledWith({ + name: 'conversation', + params: { + token: 'new-token', + }, + }) + }) + + /** + * @param {boolean} visible Whether or not the reply-private action is visible + */ + function testPrivateReplyActionVisible(visible) { + store = new Store(testStoreConfig) + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Reply privately') + expect(actionButton.exists()).toBe(visible) + } + + test('hides private reply action for own messages', async () => { + // using default message props which have the + // actor id set to the current user + testPrivateReplyActionVisible(false) + }) + + test('hides private reply action for one to one conversation type', async () => { + messageProps.actorId = 'another-user' + conversationProps.type = CONVERSATION.TYPE.ONE_TO_ONE + testPrivateReplyActionVisible(false) + }) + + test('hides private reply action for guest messages', async () => { + messageProps.actorId = 'guest-user' + messageProps.actorType = ATTENDEE.ACTOR_TYPE.GUESTS + testPrivateReplyActionVisible(false) + }) + + test('hides private reply action when current user is a guest', async () => { + messageProps.actorId = 'another-user' + getActorTypeMock.mockClear().mockReturnValue(() => ATTENDEE.ACTOR_TYPE.GUESTS) + testPrivateReplyActionVisible(false) + }) + }) + + describe('delete action', () => { + test('emits delete event', async () => { + // need to mock the date to be within 6h + const mockDate = new Date('2020-05-07 10:00:00') + jest.spyOn(global.Date, 'now') + .mockImplementation(() => mockDate) + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Delete') + expect(actionButton.exists()).toBe(true) + + await actionButton.find('button').trigger('click') + + expect(wrapper.emitted().delete).toBeTruthy() + }) + + /** + * @param {boolean} visible Whether or not the delete action is visible + * @param {Date} mockDate The message date (deletion only works within 6h) + * @param {number} participantType The participant type of the user + */ + function testDeleteMessageVisible(visible, mockDate, participantType = PARTICIPANT.TYPE.USER) { + store = new Store(testStoreConfig) + + // need to mock the date to be within 6h + if (!mockDate) { + mockDate = new Date('2020-05-07 10:00:00') + } + + jest.spyOn(global.Date, 'now') + .mockImplementation(() => mockDate) + + messageProps.participant.participantType = participantType + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Delete') + expect(actionButton.exists()).toBe(visible) + } + + test('hides delete action when message is older than 6 hours', () => { + testDeleteMessageVisible(false, new Date('2020-05-07 15:24:00')) + }) + + test('hides delete action when the conversation is read-only', () => { + conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY + testDeleteMessageVisible(false) + }) + + test('hides delete action for file messages', () => { + messageProps.message = '{file}' + messageProps.messageParameters.file = {} + testDeleteMessageVisible(false) + }) + + test('hides delete action on other people messages for non-moderators', () => { + messageProps.actorId = 'another-user' + conversationProps.type = CONVERSATION.TYPE.GROUP + testDeleteMessageVisible(false) + }) + + test('shows delete action on other people messages for moderators', () => { + messageProps.actorId = 'another-user' + conversationProps.type = CONVERSATION.TYPE.GROUP + testDeleteMessageVisible(true, null, PARTICIPANT.TYPE.MODERATOR) + }) + + test('shows delete action on other people messages for owner', () => { + messageProps.actorId = 'another-user' + conversationProps.type = CONVERSATION.TYPE.PUBLIC + testDeleteMessageVisible(true, null, PARTICIPANT.TYPE.OWNER) + }) + + test('does not show delete action even for guest moderators', () => { + messageProps.actorId = 'another-user' + conversationProps.type = CONVERSATION.TYPE.PUBLIC + testDeleteMessageVisible(false, null, PARTICIPANT.TYPE.GUEST_MODERATOR) + }) + + test('does not show delete action on other people messages in one to one conversations', () => { + messageProps.actorId = 'another-user' + conversationProps.type = CONVERSATION.TYPE.ONE_TO_ONE + testDeleteMessageVisible(false) + }) + }) + + test('marks message as unread', async () => { + const updateLastReadMessageAction = jest.fn().mockResolvedValueOnce() + const fetchConversationAction = jest.fn().mockResolvedValueOnce() + testStoreConfig.modules.conversationsStore.actions.updateLastReadMessage = updateLastReadMessageAction + testStoreConfig.modules.conversationsStore.actions.fetchConversation = fetchConversationAction + store = new Store(testStoreConfig) + + messageProps.previousMessageId = 100 + + // appears even with more restrictive conditions + conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY + messageProps.actorId = 'another-user' + messageProps.participant = { + actorId: 'guest-id-1', + actorType: ATTENDEE.ACTOR_TYPE.GUESTS, + participantType: PARTICIPANT.TYPE.GUEST, + } + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Mark as unread') + expect(actionButton.exists()).toBe(true) + + await actionButton.find('button').trigger('click') + // needs two updates... + await wrapper.vm.$nextTick() + await wrapper.vm.$nextTick() + + expect(updateLastReadMessageAction).toHaveBeenCalledWith(expect.anything(), { + token: TOKEN, + id: 100, + updateVisually: true, + }) + + expect(fetchConversationAction).toHaveBeenCalledWith(expect.anything(), { + token: TOKEN, + }) + }) + + test('copies message link', async () => { + const copyTextMock = jest.fn() + + // appears even with more restrictive conditions + conversationProps.readOnly = CONVERSATION.STATE.READ_ONLY + messageProps.actorId = 'another-user' + messageProps.participant = { + actorId: 'guest-id-1', + actorType: ATTENDEE.ACTOR_TYPE.GUESTS, + participantType: PARTICIPANT.TYPE.GUEST, + } + + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + mocks: { + $copyText: copyTextMock, + }, + stubs: { + ActionButton, + }, + + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'Copy message link') + expect(actionButton.exists()).toBe(true) + + await actionButton.find('button').trigger('click') + + expect(copyTextMock).toHaveBeenCalledWith('http://localhost/nc-webroot/call/XXTOKENXX#message_123') + }) + + test('renders clickable custom actions', async () => { + const handler = jest.fn() + const handler2 = jest.fn() + const actionsGetterMock = jest.fn().mockReturnValue([{ + label: 'first action', + icon: 'some-icon', + callback: handler, + }, { + label: 'second action', + icon: 'some-icon2', + callback: handler2, + }]) + testStoreConfig.modules.messageActionsStore.getters.messageActions = actionsGetterMock + testStoreConfig.modules.messagesStore.getters.message = jest.fn(() => () => messageProps) + store = new Store(testStoreConfig) + const wrapper = shallowMount(MessageButtonsBar, { + localVue, + store, + stubs: { + ActionButton, + }, + propsData: messageProps, + }) + + const actionButton = findActionButton(wrapper, 'first action') + expect(actionButton.exists()).toBe(true) + await actionButton.find('button').trigger('click') + + expect(handler).toHaveBeenCalledWith({ + apiDummyData: 1, + },) + + const actionButton2 = findActionButton(wrapper, 'second action') + expect(actionButton2.exists()).toBe(true) + await actionButton2.find('button').trigger('click') + + expect(handler2).toHaveBeenCalledWith({ + apiDummyData: 1, + }) + }) + }) +}) diff --git a/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue new file mode 100644 index 000000000..d1dc56aeb --- /dev/null +++ b/src/components/MessagesList/MessagesGroup/Message/MessageButtonsBar/MessageButtonsBar.vue @@ -0,0 +1,453 @@ +<!-- + - @copyright Copyright (c) 2021 Marco Ambrosini <marcoambrosini@pm.me> + - + - @author Marco Ambrosini <marcoambrosini@pm.me> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. +--> + +<template> + <!-- Message Actions --> + <div class="message-buttons-bar"> + <template v-if="page === 0"> + <Button type="tertiary" + @click="page = 1"> + <template #icon> + <EmoticonOutline :size="20" /> + </template> + </Button> + <Actions v-show="isReplyable"> + <ActionButton icon="icon-reply" + @click.stop="handleReply"> + {{ t('spreed', 'Reply') }} + </ActionButton> + </Actions> + <Actions :force-menu="true" + :container="container" + :boundaries-element="containerElement"> + <ActionButton v-if="isPrivateReplyable" + icon="icon-user" + :close-after-click="true" + @click.stop="handlePrivateReply"> + {{ t('spreed', 'Reply privately') }} + </ActionButton> + <ActionButton icon="icon-external" + :close-after-click="true" + @click.stop.prevent="handleCopyMessageLink"> + {{ t('spreed', 'Copy message link') }} + </ActionButton> + <ActionButton :close-after-click="true" + @click.stop="handleMarkAsUnread"> + <template #icon> + <EyeOffOutline decorative + title="" + :size="16" /> + </template> + {{ t('spreed', 'Mark as unread') }} + </ActionButton> + <ActionLink v-if="linkToFile" + icon="icon-text" + :href="linkToFile"> + {{ t('spreed', 'Go to file') }} + </ActionLink> + <ActionButton v-if="!isCurrentGuest && !isFileShare" + :close-after-click="true" + @click.stop="showForwarder = true"> + <Share slot="icon" + :size="16" + decorative + title="" /> + {{ t('spreed', 'Forward message') }} + </ActionButton> + <ActionSeparator v-if="messageActions.length > 0" /> + <template v-for="action in messageActions"> + <ActionButton :key="action.label" + :icon="action.icon" + :close-after-click="true" + @click="action.callback(messageApiData)"> + {{ action.label }} + </ActionButton> + </template> + <template v-if="isDeleteable"> + <ActionSeparator /> + <ActionButton icon="icon-delete" + :close-after-click="true" + @click.stop="handleDelete"> + {{ t('spreed', 'Delete') }} + </ActionButton> + </template> + </Actions> + </template> + + <template v-if="page === 1"> + <Button type="tertiary" + @click="page = 0"> + <template #icon> + <ArrowLeft :size="20" /> + </template> + </Button> + <Button type="tertiary" + @click="addReactionToMessage('👍')"> + <template #icon> + <span>👍</span> + </template> + </Button> + <Button type="tertiary" + @click="addReactionToMessage('❤️')"> + <template #icon> + <span>❤️</span> + </template> + </Button> + <EmojiPicker @select="addReactionToMessage"> + <Button type="tertiary"> + <template #icon> + <Plus :size="20" /> + </template> + </Button> + </EmojiPicker> + </template> + <Forwarder v-if="showForwarder" + :message-object="messageObject" + @close="showForwarder = false" /> + </div> +</template> + +<script> +import { PARTICIPANT, CONVERSATION, ATTENDEE } from '../../../../../constants' +import ActionButton from '@nextcloud/vue/dist/Components/ActionButton' +import ActionLink from '@nextcloud/vue/dist/Components/ActionLink' +import Actions from '@nextcloud/vue/dist/Components/Actions' +import ActionSeparator from '@nextcloud/vue/dist/Components/ActionSeparator' +import EyeOffOutline from 'vue-material-design-icons/EyeOffOutline' +import EmoticonOutline from 'vue-material-design-icons/EmoticonOutline.vue' +import ArrowLeft from 'vue-material-design-icons/ArrowLeft.vue' +import Plus from 'vue-material-design-icons/Plus.vue' +import Share from 'vue-material-design-icons/Share' +import moment from '@nextcloud/moment' +import { EventBus } from '../../../../../services/EventBus' +import { generateUrl } from '@nextcloud/router' +import { + showError, + showSuccess, +} from '@nextcloud/dialogs' +import Forwarder from '../MessagePart/Forwarder' +import Button from '@nextcloud/vue/dist/Components/Button' +import EmojiPicker from '@nextcloud/vue/dist/Components/EmojiPicker' + +export default { + name: 'MessageButtonsBar', + + components: { + Actions, + ActionButton, + ActionLink, + EyeOffOutline, + Share, + ActionSeparator, + Forwarder, + Button, + EmoticonOutline, + ArrowLeft, + Plus, + EmojiPicker, + }, + + props: { + token: { + type: String, + required: true, + }, + + previousMessageId: { + type: [String, Number], + required: true, + }, + + isReplyable: { + type: Boolean, + required: true, + }, + + messageObject: { + type: Object, + required: true, + }, + + actorId: { + type: String, + required: true, + }, + + actorType: { + type: String, + required: true, + }, + + /** + * The display name of the sender of the message. + */ + actorDisplayName: { + type: String, + required: true, + }, + + /** + * The parameters of the rich object message + */ + messageParameters: { + type: [Array, Object], + required: true, + }, + + /** + * The message timestamp. + */ + timestamp: { + type: Number, + default: 0, + }, + + /** + * The message id. + */ + id: { + type: [String, Number], + required: true, + }, + + /** + * The parent message's id. + */ + parent: { + type: Number, + default: 0, + }, + + /** + * The message or quote text. + */ + message: { + type: String, + required: true, + }, + + /** + * The type of system message + */ + systemMessage: { + type: String, + required: true, + }, + + /** + * The type of the message. + */ + messageType: { + type: String, + required: true, + }, + + /** + * The participant object. + */ + participant: { + type: Object, + required: true, + }, + + messageApiData: { + type: Object, + required: true, + }, + }, + + data() { + return { + // Shows/hides the message forwarder component + showForwarder: false, + + // The pagination of the buttons menu + page: 0, + } + }, + + computed: { + conversation() { + return this.$store.getters.conversation(this.token) + }, + + container() { + return this.$store.getters.getMainContainerSelector() + }, + + containerElement() { + return document.querySelector(this.container) + }, + + isDeleteable() { + if (this.isConversationReadOnly) { + return false + } + + const isObjectShare = this.message === '{object}' + && this.messageParameters?.object + + return (moment(this.timestamp * 1000).add(6, 'h')) > moment() + && this.messageType === 'comment' + && !this.isDeleting + && !this.isFileShare + && !isObjectShare + && (this.isMyMsg + || (this.conversation.type !== CONVERSATION.TYPE.ONE_TO_ONE + && (this.participant.participantType === PARTICIPANT.TYPE.OWNER + || this.participant.participantType === PARTICIPANT.TYPE.MODERATOR))) + }, + + isPrivateReplyable() { + return this.isReplyable + && (this.conversation.type === CONVERSATION.TYPE.PUBLIC + || this.conversation.type === CONVERSATION.TYPE.GROUP) + && !this.isMyMsg + && this.actorType === ATTENDEE.ACTOR_TYPE.USERS + && this.$store.getters.getActorType() === ATTENDEE.ACTOR_TYPE.USERS + }, + + messageActions() { + return this.$store.getters.messageActions + }, + + linkToFile() { + if (this.isFileShare) { + return this.messageParameters?.file?.link + } + return '' + }, + + isFileShare() { + return this.message === '{file}' && this.messageParameters?.file + }, + + isCurrentGuest() { + return this.$store.getters.getActorType() === 'guests' + }, + + isMyMsg() { + return this.actorId === this.$store.getters.getActorId() + && this.actorType === this.$store.getters.getActorType() + }, + + isConversationReadOnly() { + return this.conversation.readOnly === CONVERSATION.STATE.READ_ONLY + }, + }, + + methods: { + handleReply() { + this.$store.dispatch('addMessageToBeReplied', { + id: this.id, + actorId: this.actorId, + actorType: this.actorType, + actorDisplayName: this.actorDisplayName, + timestamp: this.timestamp, + systemMessage: this.systemMessage, + messageType: this.messageType, + message: this.message, + messageParameters: this.messageParameters, + token: this.token, + previousMessageId: this.previousMessageId, + }) + EventBus.$emit('focus-chat-input') + }, + + handleActionMenuUpdate(type) { + if (type === 'open') { + this.isActionMenuOpen = true + } else if (type === 'close') { + this.isActionMenuOpen = false + this.showActions = false + } + }, + + async handlePrivateReply() { + // open the 1:1 conversation + const conversation = await this.$store.dispatch('createOneToOneConversation', this.actorId) + this.$router.push({ name: 'conversation', params: { token: conversation.token } }).catch(err => console.debug(`Error while pushing the new conversation's route: ${err}`)) + }, + + async handleCopyMessageLink() { + try { + const link = window.location.protocol + '//' + window.location.host + generateUrl('/call/' + this.token) + '#message_' + this.id + await this.$copyText(link) + showSuccess(t('spreed', 'Message link copied to clipboard.')) + } catch (error) { + console.error('Error copying link: ', error) + showError(t('spreed', 'The link could not be copied.')) + } + }, + + async handleMarkAsUnread() { + // update in backend + visually + await this.$store.dispatch('updateLastReadMessage', { + token: this.token, + id: this.previousMessageId, + updateVisually: true, + }) + + // reload conversation to update additional attributes that have computed values + await this.$store.dispatch('fetchConversation', { token: this.token }) + }, + + addReactionToMessage(selectedEmoji) { + // Add reaction only if user hasn't reacted yet + if (!this.$store.getters.userHasReacted(this.actorId, this.token, this.messageObject.id, selectedEmoji)) { + this.$store.dispatch('addReactionToMessage', { + token: this.token, + messageId: this.messageObject.id, + selectedEmoji, + actorId: this.actorId, + }) + } else { + console.debug('Current user has already reacted') + } + + }, + + handleDelete() { + this.$emit('delete') + }, + }, +} +</script> + +<style lang="scss" scoped> +@import '../../../../../assets/variables'; + +.message-buttons-bar { + display: flex; + right: 14px; + bottom: -4px; + position: absolute; + z-index: 100000; + background-color: var(--color-main-background); + border-radius: calc($clickable-area / 2); + box-shadow: 0 0 4px 0px var(--color-box-shadow); + + & h6 { + margin-left: auto; + } +} + +</style> diff --git a/src/components/Quote.vue b/src/components/Quote.vue index d4a0e573d..6a19bc504 100644 --- a/src/components/Quote.vue +++ b/src/components/Quote.vue @@ -81,6 +81,9 @@ export default { type: String, required: true, }, + /** + * The display name of the sender of the message. + */ actorDisplayName: { type: String, required: true, diff --git a/src/services/messagesService.js b/src/services/messagesService.js index 938a35130..3a211a7e5 100644 --- a/src/services/messagesService.js +++ b/src/services/messagesService.js @@ -132,6 +132,24 @@ const updateLastReadMessage = async function(token, lastReadMessage) { }) } +const addReactionToMessage = async function(token, messageId, selectedEmoji) { + return axios.post(generateOcsUrl('apps/spreed/api/v1/reaction/{token}/{messageId}', { token, messageId }), { + reaction: selectedEmoji, + }) +} + +const removeReactionFromMessage = async function(token, messageId, selectedEmoji) { + return axios.delete(generateOcsUrl('apps/spreed/api/v1/reaction/{token}/{messageId}', { token, messageId }), { + params: { + reaction: selectedEmoji, + }, + }) +} + +const getReactionsDetails = async function(token, messageId) { + return axios.get(generateOcsUrl('apps/spreed/api/v1/reaction/{token}/{messageId}', { token, messageId })) +} + export { fetchMessages, lookForNewMessages, @@ -139,4 +157,7 @@ export { deleteMessage, postRichObjectToConversation, updateLastReadMessage, + addReactionToMessage, + removeReactionFromMessage, + getReactionsDetails, } diff --git a/src/store/messagesStore.js b/src/store/messagesStore.js index 3c08544e3..3dd4833d3 100644 --- a/src/store/messagesStore.js +++ b/src/store/messagesStore.js @@ -27,6 +27,8 @@ import { lookForNewMessages, postNewMessage, postRichObjectToConversation, + addReactionToMessage, + removeReactionFromMessage, } from '../services/messagesService' import SHA256 from 'crypto-js/sha256' @@ -138,7 +140,14 @@ const getters = { */ messagesList: (state) => (token) => { if (state.messages[token]) { - return Object.values(state.messages[token]) + return Object.values(state.messages[token]).filter(message => { + // Filter out reaction messages + if (message.systemMessage === 'reaction' || message.systemMessage === 'reaction_deleted' || message.systemMessage === 'reaction_revoked') { + return false + } else { + return true + } + }) } return [] }, @@ -191,6 +200,11 @@ const getters = { // the cancel handler only exists when a message is being sent return Object.keys(state.cancelPostNewMessage).length !== 0 }, + + // Returns true if the message has reactions + hasReactions: (state) => (token, messageId) => { + return Object.keys(state.messages[token][messageId].reactions).length !== 0 + }, } const mutations = { @@ -333,6 +347,24 @@ const mutations = { Vue.delete(state.messages, token) } }, + + // Increases reaction count for a particular reaction on a message + addReactionToMessage(state, { token, messageId, reaction }) { + if (!state.messages[token][messageId].reactions[reaction]) { + Vue.set(state.messages[token][messageId].reactions, reaction, 0) + } + const reactionCount = state.messages[token][messageId].reactions[reaction] + 1 + Vue.set(state.messages[token][messageId].reactions, reaction, reactionCount) + }, + + // Decreases reaction count for a particular reaction on a message + removeReactionFromMessage(state, { token, messageId, reaction }) { + const reactionCount = state.messages[token][messageId].reactions[reaction] - 1 + Vue.set(state.messages[token][messageId].reactions, reaction, reactionCount) + if (state.messages[token][messageId].reactions[reaction] <= 0) { + Vue.delete(state.messages[token][messageId].reactions, reaction) + } + }, } const actions = { @@ -442,6 +474,7 @@ const actions = { token, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: Hex.stringify(SHA256(tempId)), }) @@ -931,6 +964,72 @@ const actions = { const response = await postRichObjectToConversation(token, richObject) return response }, + + /** + * Adds a single reaction to a message for the current user. + * + * @param {*} context the context object + * @param {*} param1 conversation token, message id and selected emoji (string) + */ + async addReactionToMessage(context, { token, messageId, selectedEmoji }) { + try { + context.commit('addReactionToMessage', { + token, + messageId, + reaction: selectedEmoji, + }) + // The response return an array with the reaction details for this message + const response = await addReactionToMessage(token, messageId, selectedEmoji) + // We replace the reaction details in the reactions store and wipe the old + // values + context.dispatch('updateReactions', { + token, + messageId, + reactionsDetails: response.data.ocs.data, + }) + } catch (error) { + // Restore the previous state if the request fails + context.commit('removeReactionFromMessage', { + token, + messageId, + reaction: selectedEmoji, + }) + console.debug(error) + } + }, + + /** + * Removes a single reaction from a message for the current user. + * + * @param {*} context the context object + * @param {*} param1 conversation token, message id and selected emoji (string) + */ + async removeReactionFromMessage(context, { token, messageId, selectedEmoji }) { + try { + context.commit('removeReactionFromMessage', { + token, + messageId, + reaction: selectedEmoji, + }) + // The response return an array with the reaction details for this message + const response = await removeReactionFromMessage(token, messageId, selectedEmoji) + // We replace the reaction details in the reactions store and wipe the old + // values + context.dispatch('updateReactions', { + token, + messageId, + reactionsDetails: response.data.ocs.data, + }) + } catch (error) { + // Restore the previous state if the request fails + context.commit('addReactionToMessage', { + token, + messageId, + reaction: selectedEmoji, + }) + console.debug(error) + } + }, } export default { state, mutations, getters, actions } diff --git a/src/store/messagesStore.spec.js b/src/store/messagesStore.spec.js index 354d5e579..f7d715aee 100644 --- a/src/store/messagesStore.spec.js +++ b/src/store/messagesStore.spec.js @@ -313,6 +313,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), }) }) @@ -345,6 +346,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), parent: 123, }) @@ -389,6 +391,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), }) }) @@ -418,6 +421,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), }]) @@ -440,6 +444,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), }]) }) @@ -473,6 +478,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: 'failure-reason', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), }]) }) @@ -518,6 +524,7 @@ describe('messagesStore', () => { token: TOKEN, isReplyable: false, sendingFailure: '', + reactions: {}, referenceId: expect.stringMatching(/^[a-zA-Z0-9]{64}$/), }]) }) diff --git a/src/store/reactionsStore.js b/src/store/reactionsStore.js new file mode 100644 index 000000000..821e6c60a --- /dev/null +++ b/src/store/reactionsStore.js @@ -0,0 +1,103 @@ +/** + * @copyright Copyright (c) 2022 Marco Ambrosini <marcoambrosini@pm.me> + * + * @author Marco Ambrosini <marcoambrosini@pm.me> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +import Vue from 'vue' +import { + getReactionsDetails, +} from '../services/messagesService' + +const state = { + /** + * Structure: + * reactions.token.messageId + */ + reactions: {}, +} + +const getters = { + reactions: (state) => (token, messageId) => { + if (state.reactions?.[token]?.[messageId]) { + return state.reactions[token][messageId] + } else { + return undefined + } + }, + + // Checks if a user has already reacted to a message with a particular reaction + userHasReacted: (state) => (actorId, token, messageId, reaction) => { + if (!state?.reactions?.[token]?.[messageId]?.[reaction]) { + return false + } + return state?.reactions?.[token]?.[messageId]?.[reaction].filter(item => { + return item.actorId === actorId + }).length !== 0 + }, +} + +const mutations = { + addReactions(state, { token, messageId, reactions }) { + if (!state.reactions[token]) { + Vue.set(state.reactions, token, {}) + + } + Vue.set(state.reactions[token], messageId, reactions) + }, +} + +const actions = { + /** + * Updates reactions for a given message. + * + * @param {*} context The context object + * @param {*} param1 conversation token, message id + */ + async updateReactions(context, { token, messageId, reactionsDetails }) { + context.commit('addReactions', { + token, + messageId, + reactions: reactionsDetails, + }) + }, + + /** + * Gets the full reactions array for a given message. + * + * @param {*} context the context object + * @param {*} param1 conversation token, message id + */ + async getReactions(context, { token, messageId }) { + console.debug('getting reactions details') + try { + const response = await getReactionsDetails(token, messageId) + context.commit('addReactions', { + token, + messageId, + reactions: response.data.ocs.data, + }) + + return response + } catch (error) { + console.debug(error) + } + }, +} + +export default { state, mutations, getters, actions } diff --git a/src/store/storeConfig.js b/src/store/storeConfig.js index 16b8b6db4..8d772fa26 100644 --- a/src/store/storeConfig.js +++ b/src/store/storeConfig.js @@ -38,6 +38,7 @@ import tokenStore from './tokenStore' import uiModeStore from './uiModeStore' import windowVisibilityStore from './windowVisibilityStore' import messageActionsStore from './messageActionsStore' +import reactionsStore from './reactionsStore' export default { modules: { @@ -59,6 +60,7 @@ export default { uiModeStore, windowVisibilityStore, messageActionsStore, + reactionsStore, }, mutations: {}, |