diff --git a/.github/workflows/cypress.yml b/.github/workflows/cypress.yml index f101d15efe0..c4bebac631a 100644 --- a/.github/workflows/cypress.yml +++ b/.github/workflows/cypress.yml @@ -75,11 +75,21 @@ jobs: node-version: [16] containers: [1, 2, 3, 4, 5, 6, 7, 8] php-versions: [ '8.1' ] - databases: [ 'sqlite' ] server-versions: ['stable26'] name: runner ${{ matrix.containers }} + services: + postgres: + image: ghcr.io/nextcloud/continuous-integration-postgres-14:latest + ports: + - 4444:5432/tcp + env: + POSTGRES_USER: root + POSTGRES_PASSWORD: rootpassword + POSTGRES_DB: nextcloud + options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 + steps: - name: Restore context uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1 @@ -101,7 +111,7 @@ jobs: uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php-versions }} - extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, apcu + extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql ini-values: apc.enable_cli=on coverage: none @@ -113,7 +123,7 @@ jobs: run: | mkdir data echo '"\OC\Memcache\APCu","hashing_default_password"=>true];' > config/config.php - php occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin + php occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin php -f index.php php -S 0.0.0.0:8081 & export OC_PASS=1234561 diff --git a/.github/workflows/phpunit-mysql.yml b/.github/workflows/phpunit-mysql.yml index 128a57b0e12..194ffb7c29d 100644 --- a/.github/workflows/phpunit-mysql.yml +++ b/.github/workflows/phpunit-mysql.yml @@ -56,11 +56,6 @@ jobs: # Split and keep last echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - name: Enable ONLY_FULL_GROUP_BY MySQL option - run: | - echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - echo "SELECT @@sql_mode;" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - - name: Checkout server uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2 with: @@ -73,6 +68,11 @@ jobs: with: path: apps/${{ env.APP_NAME }} + - name: Enable ONLY_FULL_GROUP_BY MySQL option + run: | + echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword + echo "SELECT @@sql_mode;" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword + - name: Set up php ${{ matrix.php-versions }} uses: shivammathur/setup-php@c5fc0d8281aba02c7fda07d3a70cc5371548067d # v2 with: diff --git a/.github/workflows/phpunit-oci.yml b/.github/workflows/phpunit-oci.yml index 3f9c5c9071f..9b792b79adb 100644 --- a/.github/workflows/phpunit-oci.yml +++ b/.github/workflows/phpunit-oci.yml @@ -34,7 +34,7 @@ concurrency: jobs: phpunit-oci: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: diff --git a/cypress/e2e/attachments.spec.js b/cypress/e2e/attachments.spec.js index f8ffe0bc1a5..86dffe7e57e 100644 --- a/cypress/e2e/attachments.spec.js +++ b/cypress/e2e/attachments.spec.js @@ -21,7 +21,6 @@ */ import { initUserAndFiles, randHash, randUser } from '../utils/index.js' -import 'cypress-file-upload' const user = randUser() const recipient = randUser() @@ -41,7 +40,12 @@ function attachFile(name, requestAlias = null) { } return cy.getEditor() .find('input[type="file"][data-text-el="attachment-file-input"]') - .attachFile(name) + .selectFile([ + { + contents: 'cypress/fixtures/' + name, + fileName: name, + }, + ], { force: true }) } /** @@ -406,10 +410,4 @@ describe('Test all attachment insertion methods', () => { } }) }) - - it('Delete the user', () => { - cy.deleteUser(user) - cy.deleteUser(recipient) - }) - }) diff --git a/cypress/e2e/nodes/ImageView.spec.js b/cypress/e2e/nodes/ImageView.spec.js index ad0c78591d4..eab983f4ec6 100644 --- a/cypress/e2e/nodes/ImageView.spec.js +++ b/cypress/e2e/nodes/ImageView.spec.js @@ -15,7 +15,6 @@ describe('Image View', () => { before(() => { cy.createUser(user) cy.login(user) - cy.visit('/apps/files') // Upload test files to user's storage cy.createFolder('child-folder') diff --git a/cypress/e2e/nodes/Mentions.spec.js b/cypress/e2e/nodes/Mentions.spec.js index 25c0ce69cbf..e732ea866b3 100644 --- a/cypress/e2e/nodes/Mentions.spec.js +++ b/cypress/e2e/nodes/Mentions.spec.js @@ -1,5 +1,4 @@ import { initUserAndFiles, randUser } from '../../utils/index.js' -import 'cypress-file-upload' const user = randUser() const mentionMe = randUser() diff --git a/cypress/e2e/sync.spec.js b/cypress/e2e/sync.spec.js index 0d09003b264..ba2b401e536 100644 --- a/cypress/e2e/sync.spec.js +++ b/cypress/e2e/sync.spec.js @@ -66,26 +66,31 @@ describe('Sync', () => { it('recovers from a lost connection', () => { let count = 0 - cy.intercept({ method: 'POST', url: '**/apps/text/session/push' }, (req) => { - if (count < 5) { - count++ + cy.intercept({ method: 'PUT', url: '**/apps/text/session/create' }) + .as('createSession') + cy.intercept({ method: 'POST', url: '**/apps/text/session/*' }, (req) => { + if (count < 4) { req.destroy() req.alias = 'dead' + } else { + req.alias = 'alive' } - }).as('push') - cy.intercept({ method: 'POST', url: '**/apps/text/session/sync' }, (req) => { - if (count < 5) { - count++ - req.destroy() - req.alias = 'deadSync' - } - }) - cy.get('#editor-container .document-status') - .should('contain', 'File could not be loaded', { timeout: 10000 }) - cy.get('#editor-container .document-status', { timeout: 30000 }) - .should('not.contain', 'File could not be loaded') + }).as('sessionRequests') + cy.wait('@dead', { timeout: 30000 }) + cy.get('#editor-container .document-status', { timeout: 10000 }) + .should('contain', 'File could not be loaded') + .then(() => { + count = 4 + }) + cy.wait('@alive', { timeout: 30000 }) cy.intercept({ method: 'POST', url: '**/apps/text/session/sync' }) .as('syncAfterRecovery') + cy.wait('@syncAfterRecovery', { timeout: 30000 }) + cy.get('#editor-container .document-status', { timeout: 30000 }) + .should('not.contain', 'File could not be loaded') + // FIXME: There seems to be a bug where typed words maybe lost if not waiting for the new session + cy.wait('@createSession') + cy.wait('@syncAfterRecovery') cy.getContent().type('* more content added after the lost connection{enter}') cy.wait('@syncAfterRecovery') cy.closeFile() diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 56c1996a690..9d1276d7edd 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -36,48 +36,61 @@ addCommands() // and also to determine paths, urls and the like. let auth Cypress.Commands.overwrite('login', (login, user) => { + cy.window().then((win) => { + win.location.href = 'about:blank' + }) auth = { user: user.userId, password: user.password } login(user) }) Cypress.Commands.add('ocsRequest', (options) => { - return cy.request({ - form: true, - auth, - headers: { - 'OCS-ApiRequest': 'true', - 'Content-Type': 'application/x-www-form-urlencoded', - }, - ...options, - }) + return cy.request('/csrftoken') + .then(({ body }) => body.token) + .then(requesttoken => { + return cy.request({ + form: true, + auth, + headers: { + 'OCS-ApiRequest': 'true', + 'Content-Type': 'application/x-www-form-urlencoded', + requesttoken, + }, + ...options, + }) + }) }) Cypress.Commands.add('uploadFile', (fileName, mimeType, target) => { - return cy.fixture(fileName, 'base64') - .then(Cypress.Blob.base64StringToBlob) + return cy.fixture(fileName, 'binary') + .then(Cypress.Blob.binaryStringToBlob) .then(blob => { - const file = new File([blob], fileName, { type: mimeType }) if (typeof target !== 'undefined') { fileName = target } - return cy.request('/csrftoken') - .then(({ body }) => body.token) - .then(requesttoken => { - return axios.put(`${url}/remote.php/webdav/${fileName}`, file, { + cy.request('/csrftoken') + .then(({ body }) => { + return cy.wrap(body.token) + }) + .then(async (requesttoken) => { + return cy.request({ + url: `${url}/remote.php/webdav/${fileName}`, + method: 'put', + body: blob.size > 0 ? blob : '', + auth, headers: { requesttoken, 'Content-Type': mimeType, }, - }).then(response => { - const fileId = Number( - response.headers['oc-fileid']?.split('oc')?.[0] - ) - cy.log(`Uploaded ${fileName}`, - response.status, - { fileId } - ) - return fileId }) + }).then(response => { + const fileId = Number( + response.headers['oc-fileid']?.split('oc')?.[0] + ) + cy.log(`Uploaded ${fileName}`, + response.status, + { fileId } + ) + return cy.wrap(fileId) }) }) }) @@ -95,27 +108,27 @@ Cypress.Commands.add('downloadFile', (fileName) => { }) Cypress.Commands.add('createFile', (target, content, mimeType = 'text/markdown') => { - const fileName = target.split('/').pop() - const blob = new Blob([content], { type: mimeType }) - const file = new File([blob], fileName, { type: mimeType }) - return cy.window() - .then(async win => { - const response = await axios.put(`${url}/remote.php/webdav/${target}`, file, { + return cy.request('/csrftoken') + .then(({ body }) => body.token) + .then(requesttoken => { + return cy.request({ + url: `${url}/remote.php/webdav/${target}`, + method: 'put', + body: blob.size > 0 ? blob : '', + auth, headers: { - requesttoken: win.OC.requestToken, 'Content-Type': mimeType, + requesttoken, }, + }).then((response) => { + return cy.log(`Uploaded ${target}`, response.status) }) - - return cy.log(`Uploaded ${fileName}`, response.status) }) - }) Cypress.Commands.add('shareFileToUser', (path, targetUser, shareData = {}) => { - cy.clearCookies() cy.ocsRequest({ method: 'POST', url: `${url}/ocs/v2.php/apps/files_sharing/api/v1/shares`, @@ -212,7 +225,8 @@ Cypress.Commands.add('createFolder', (target) => { return cy.request('/csrftoken') .then(({ body }) => body.token) .then(requesttoken => { - return axios.request(`${rootPath}/${dirPath}`, { + return cy.request({ + url: `${rootPath}/${dirPath}`, method: 'MKCOL', auth, headers: { diff --git a/package-lock.json b/package-lock.json index f0e535cb854..9187bf48a90 100644 --- a/package-lock.json +++ b/package-lock.json @@ -95,7 +95,6 @@ "@vue/test-utils": "^1.3.0 <2", "@vue/vue2-jest": "^29.2.4", "cypress": "^12.15.0", - "cypress-file-upload": "^5.0.8", "eslint-plugin-cypress": "^2.13.3", "identity-obj-proxy": "^3.0.0", "jest": "^29.5.0", @@ -7975,18 +7974,6 @@ "node": "^14.0.0 || ^16.0.0 || >=18.0.0" } }, - "node_modules/cypress-file-upload": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", - "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", - "dev": true, - "engines": { - "node": ">=8.2.1" - }, - "peerDependencies": { - "cypress": ">3.0.0" - } - }, "node_modules/cypress/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -27746,13 +27733,6 @@ } } }, - "cypress-file-upload": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", - "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", - "dev": true, - "requires": {} - }, "dash-ast": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/dash-ast/-/dash-ast-1.0.0.tgz", diff --git a/package.json b/package.json index f97e42242d6..683581a7b7c 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,6 @@ "@vue/test-utils": "^1.3.0 <2", "@vue/vue2-jest": "^29.2.4", "cypress": "^12.15.0", - "cypress-file-upload": "^5.0.8", "eslint-plugin-cypress": "^2.13.3", "identity-obj-proxy": "^3.0.0", "jest": "^29.5.0",