From e1d69e236fba90279f2931502eeaecbae8d23703 Mon Sep 17 00:00:00 2001 From: tamaina Date: Fri, 21 Jan 2022 16:43:56 +0900 Subject: [PATCH] =?UTF-8?q?enhance:=20e2e=E3=83=86=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E3=81=A7=E3=81=8D=E3=82=8B=E3=81=A0=E3=81=91=E6=94=B9?= =?UTF-8?q?=E8=89=AF=E3=81=97=E3=81=A6=E3=81=BF=E3=81=9F=20(#8159)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * update docker image? * 続 * serial run delete from "${table}" cascade * use cypress official github action * refuse install by cypress action * clean up * use wait? * use more wait? * Revert "use more wait?" This reverts commit 18d0fcae9c7d8f98a4cafb4a846a031ece57350c. * Revert "use wait?" This reverts commit 5aa8feec9cdc3e2f79e566249f0a0eff6c0df6a0. * fix * test * test * log? * 握りつぶしてみる * clean up * env? * clean up? * disable video * add comment * remove test * 成功? * test browser * nodeインストール無効化 * node16.13.0-chrome95-ff94 * node.js復活 * ? * ちょっと戻してみる * chrome? * cross browser test2 * --shm-size=2g * artifact? * misskey.local? * firefoxはあきらめる * not headless? * oops * fix * ?? * test1 * if? * fail-fast: false * headless: false * easy error ignoreing describe * エラーの解消 とちょっとリファクター * add browser name to artifact * Install mplayer for FireFox * no wait? * タイムアウトを甘くしてみる * firefoxをあきらめる(n回目) * remove timeout setting * wait復活 * Update basic.js * Update index.js Co-authored-by: syuilo --- .github/workflows/test.yml | 39 ++++++++++++--- cypress/integration/basic.js | 44 +++++++---------- cypress/support/index.js | 12 +++-- packages/backend/src/db/postgre.ts | 4 +- packages/backend/test/docker-compose.yml | 4 +- packages/client/src/components/notes.vue | 4 +- packages/client/src/components/timeline.vue | 12 ++--- .../client/src/components/ui/pagination.vue | 47 +++++++++++-------- packages/client/src/pages/tag.vue | 2 +- .../client/src/pages/user/index.timeline.vue | 2 +- 10 files changed, 99 insertions(+), 71 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6a684eaa48..3e9585f96d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,14 +17,14 @@ jobs: services: postgres: - image: postgres:12.2-alpine + image: postgres:13 ports: - 54312:5432 env: POSTGRES_DB: test-misskey POSTGRES_HOST_AUTH_METHOD: trust redis: - image: redis:4.0-alpine + image: redis:6 ports: - 56312:6379 @@ -51,19 +51,21 @@ jobs: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: node-version: [16.x] + browser: [chrome] services: postgres: - image: postgres:12.2-alpine + image: postgres:13 ports: - 54312:5432 env: POSTGRES_DB: test-misskey POSTGRES_HOST_AUTH_METHOD: trust redis: - image: redis:4.0-alpine + image: redis:6 ports: - 56312:6379 @@ -71,6 +73,12 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + # https://github.com/cypress-io/cypress-docker-images/issues/150 + #- name: Install mplayer for FireFox + # run: sudo apt install mplayer -y + # if: ${{ matrix.browser == 'firefox' }} + #- uses: browser-actions/setup-firefox@latest + # if: ${{ matrix.browser == 'firefox' }} - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v1 with: @@ -87,5 +95,24 @@ jobs: run: cp .github/misskey/test.yml .config - name: Build run: yarn build - - name: Test - run: yarn e2e + # https://github.com/cypress-io/cypress/issues/4351#issuecomment-559489091 + - name: ALSA Env + run: echo -e 'pcm.!default {\n type hw\n card 0\n}\n\nctl.!default {\n type hw\n card 0\n}' > ~/.asoundrc + - name: Cypress run + uses: cypress-io/github-action@v2 + with: + install: false + start: npm run start:test + wait-on: 'http://localhost:61812' + headless: false + browser: ${{ matrix.browser }} + - uses: actions/upload-artifact@v2 + if: failure() + with: + name: ${{ matrix.browser }}-cypress-screenshots + path: cypress/screenshots + - uses: actions/upload-artifact@v2 + if: always() + with: + name: ${{ matrix.browser }}-cypress-videos + path: cypress/videos diff --git a/cypress/integration/basic.js b/cypress/integration/basic.js index a754f41b98..aca44ef15d 100644 --- a/cypress/integration/basic.js +++ b/cypress/integration/basic.js @@ -41,8 +41,6 @@ describe('After setup instance', () => { username: 'admin', password: 'pass', }).its('body').as('admin'); - - cy.get('@admin'); }); afterEach(() => { @@ -82,15 +80,11 @@ describe('After user signup', () => { password: 'pass', }).its('body').as('admin'); - cy.get('@admin').then(() => { - // ユーザー作成 - cy.request('POST', '/api/signup', { - username: 'alice', - password: 'alice1234', - }).its('body').as('alice'); - }); - - cy.get('@alice'); + // ユーザー作成 + cy.request('POST', '/api/signup', { + username: 'alice', + password: 'alice1234', + }).its('body').as('alice'); }); afterEach(() => { @@ -145,27 +139,21 @@ describe('After user singed in', () => { password: 'pass', }).its('body').as('admin'); - cy.get('@admin').then(() => { - // ユーザー作成 - cy.request('POST', '/api/signup', { - username: 'alice', - password: 'alice1234', - }).its('body').as('alice'); - }); + // ユーザー作成 + cy.request('POST', '/api/signup', { + username: 'alice', + password: 'alice1234', + }).its('body').as('alice'); - cy.get('@alice').then(() => { - cy.visit('/'); + cy.visit('/'); - cy.intercept('POST', '/api/signin').as('signin'); + cy.intercept('POST', '/api/signin').as('signin'); - cy.get('[data-cy-signin]').click(); - cy.get('[data-cy-signin-username] input').type('alice'); - cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); + cy.get('[data-cy-signin]').click(); + cy.get('[data-cy-signin-username] input').type('alice'); + cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); - cy.wait('@signin').as('signedIn'); - }); - - cy.get('@signedIn'); + cy.wait('@signin').as('signedIn'); }); afterEach(() => { diff --git a/cypress/support/index.js b/cypress/support/index.js index a9ac34476d..9185be344c 100644 --- a/cypress/support/index.js +++ b/cypress/support/index.js @@ -20,7 +20,13 @@ import './commands' // require('./commands') Cypress.on('uncaught:exception', (err, runnable) => { - if (err.message.includes('ResizeObserver loop limit exceeded')) { - return false - } + if ([ + // Chrome + 'ResizeObserver loop limit exceeded', + + // Firefox + 'ResizeObserver loop completed with undelivered notifications', + ].some(msg => err.message.includes(msg))) { + return false; + } }); diff --git a/packages/backend/src/db/postgre.ts b/packages/backend/src/db/postgre.ts index 1692b26219..69336c2a46 100644 --- a/packages/backend/src/db/postgre.ts +++ b/packages/backend/src/db/postgre.ts @@ -220,7 +220,9 @@ export async function resetDb() { WHERE nspname NOT IN ('pg_catalog', 'information_schema') AND C.relkind = 'r' AND nspname !~ '^pg_toast';`); - await Promise.all(tables.map(t => t.table).map(x => conn.query(`DELETE FROM "${x}" CASCADE`))); + for (const table of tables) { + await conn.query(`DELETE FROM "${table.table}" CASCADE`); + } }; for (let i = 1; i <= 3; i++) { diff --git a/packages/backend/test/docker-compose.yml b/packages/backend/test/docker-compose.yml index c045e7c6c4..5f95bec4c0 100644 --- a/packages/backend/test/docker-compose.yml +++ b/packages/backend/test/docker-compose.yml @@ -2,12 +2,12 @@ version: "3" services: redistest: - image: redis:4.0-alpine + image: redis:6 ports: - "127.0.0.1:56312:6379" dbtest: - image: postgres:12.2-alpine + image: postgres:13 ports: - "127.0.0.1:54312:5432" environment: diff --git a/packages/client/src/components/notes.vue b/packages/client/src/components/notes.vue index aec478ac95..41bec5a579 100644 --- a/packages/client/src/components/notes.vue +++ b/packages/client/src/components/notes.vue @@ -32,9 +32,7 @@ const props = defineProps<{ const pagingComponent = ref>(); defineExpose({ - prepend: (note) => { - pagingComponent.value?.prepend(note); - }, + pagingComponent, }); diff --git a/packages/client/src/components/timeline.vue b/packages/client/src/components/timeline.vue index a7af02c30b..59956b9526 100644 --- a/packages/client/src/components/timeline.vue +++ b/packages/client/src/components/timeline.vue @@ -25,10 +25,10 @@ const emit = defineEmits<{ provide('inChannel', computed(() => props.src === 'channel')); -const tlComponent = ref>(); +const tlComponent: InstanceType = $ref(); const prepend = note => { - tlComponent.value.prepend(note); + tlComponent.pagingComponent?.prepend(note); emit('note'); @@ -38,16 +38,16 @@ const prepend = note => { }; const onUserAdded = () => { - tlComponent.value.reload(); + tlComponent.pagingComponent?.reload(); }; const onUserRemoved = () => { - tlComponent.value.reload(); + tlComponent.pagingComponent?.reload(); }; const onChangeFollowing = () => { - if (!tlComponent.value.backed) { - tlComponent.value.reload(); + if (!tlComponent.pagingComponent?.backed) { + tlComponent.pagingComponent?.reload(); } }; diff --git a/packages/client/src/components/ui/pagination.vue b/packages/client/src/components/ui/pagination.vue index 571ef71eab..9c18fc5ce5 100644 --- a/packages/client/src/components/ui/pagination.vue +++ b/packages/client/src/components/ui/pagination.vue @@ -73,12 +73,11 @@ const queue = ref([]); const offset = ref(0); const fetching = ref(true); const moreFetching = ref(false); -const inited = ref(false); const more = ref(false); const backed = ref(false); // 遡り中か否か const isBackTop = ref(false); -const empty = computed(() => items.value.length === 0 && !fetching.value && inited.value); -const error = computed(() => !fetching.value && !inited.value); +const empty = computed(() => items.value.length === 0); +const error = ref(false); const init = async (): Promise => { queue.value = []; @@ -105,9 +104,10 @@ const init = async (): Promise => { more.value = false; } offset.value = res.length; - inited.value = true; + error.value = false; fetching.value = false; }, e => { + error.value = true; fetching.value = false; }); }; @@ -183,30 +183,36 @@ const fetchMoreAhead = async (): Promise => { }; const prepend = (item: Item): void => { - if (rootEl.value == null) return; - if (props.pagination.reversed) { - const container = getScrollContainer(rootEl.value); - if (container == null) return; // TODO? + if (rootEl.value) { + const container = getScrollContainer(rootEl.value); + if (container == null) return; // TODO? - const pos = getScrollPosition(rootEl.value); - const viewHeight = container.clientHeight; - const height = container.scrollHeight; - const isBottom = (pos + viewHeight > height - 32); - if (isBottom) { - // オーバーフローしたら古いアイテムは捨てる - if (items.value.length >= props.displayLimit) { - // このやり方だとVue 3.2以降アニメーションが動かなくなる - //items.value = items.value.slice(-props.displayLimit); - while (items.value.length >= props.displayLimit) { - items.value.shift(); + const pos = getScrollPosition(rootEl.value); + const viewHeight = container.clientHeight; + const height = container.scrollHeight; + const isBottom = (pos + viewHeight > height - 32); + if (isBottom) { + // オーバーフローしたら古いアイテムは捨てる + if (items.value.length >= props.displayLimit) { + // このやり方だとVue 3.2以降アニメーションが動かなくなる + //items.value = items.value.slice(-props.displayLimit); + while (items.value.length >= props.displayLimit) { + items.value.shift(); + } + more.value = true; } - more.value = true; } } items.value.push(item); // TODO } else { + // 初回表示時はunshiftだけでOK + if (!rootEl.value) { + items.value.unshift(item); + return; + } + const isTop = isBackTop.value || (document.body.contains(rootEl.value) && isTopVisible(rootEl.value)); if (isTop) { @@ -264,6 +270,7 @@ onDeactivated(() => { defineExpose({ items, + backed, reload, fetchMoreAhead, prepend, diff --git a/packages/client/src/pages/tag.vue b/packages/client/src/pages/tag.vue index 8d8dc0a65c..045f1ef259 100644 --- a/packages/client/src/pages/tag.vue +++ b/packages/client/src/pages/tag.vue @@ -1,6 +1,6 @@ diff --git a/packages/client/src/pages/user/index.timeline.vue b/packages/client/src/pages/user/index.timeline.vue index 7396a76efe..a1329a7411 100644 --- a/packages/client/src/pages/user/index.timeline.vue +++ b/packages/client/src/pages/user/index.timeline.vue @@ -5,7 +5,7 @@ - +