feat(tests): add e2e tests for widgets (#8735)

* test(e2e): add baseline for widget tests

* chore(repo): enable test running in branch

* fix(e2e): set viewport for widget tests

* fix(client): add widget identifier classes to widgets

* test(e2e): add memo widget test

* fix(tests): force select value

* fix(tests): force button press for widget addition

* fix(tests): invoke select value differently

* fix(tests): adjust widget submit

* fix(tests): don't explicitly navigate for widget test

* fix(tests): click label to hide select popup

* fix(tests): just click modal background

* fix(tests): adjust modal background selector

* fix(tests): click all modal backgrounds

* feat(e2e): add test for adding timeline widget

* fix(client): add more widget identifier classes

* feat(tests): add method abstraction for test cases

* fix(tests): force-click overlays

* fix(tests): force widget button press

* fix(tests): remove timeout from final widget check

* feat(tests): add widget removal test case

* fix(client): use mk instead of msky as class prefix

* fix(tests): check widgets for existence rather than visibility

* chore(meta): don't run tests for specific feature branch
This commit is contained in:
Andy 2022-05-28 07:28:12 +02:00 committed by GitHub
parent 1c057818c6
commit 708fba989a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 100 additions and 16 deletions

View File

@ -0,0 +1,84 @@
describe('After user signed in', () => {
beforeEach(() => {
cy.window(win => {
win.indexedDB.deleteDatabase('keyval-store');
});
cy.viewport('macbook-16');
cy.request('POST', '/api/reset-db').as('reset');
cy.get('@reset').its('status').should('equal', 204);
cy.reload(true);
// インスタンス初期セットアップ
cy.request('POST', '/api/admin/accounts/create', {
username: 'admin',
password: 'pass',
}).its('body').as('admin');
// ユーザー作成
cy.request('POST', '/api/signup', {
username: 'alice',
password: 'alice1234',
}).its('body').as('alice');
cy.visit('/');
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.wait('@signin').as('signedIn');
});
afterEach(() => {
// テスト終了直前にページ遷移するようなテストケース(例えばアカウント作成)だと、たぶんCypressのバグでブラウザの内容が次のテストケースに引き継がれてしまう(例えばアカウントが作成し終わった段階からテストが始まる)。
// waitを入れることでそれを防止できる
cy.wait(1000);
});
it('widget edit toggle is visible', () => {
cy.get('.mk-widget-edit').should('be.visible');
});
it('widget select should be visible in edit mode', () => {
cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select').should('be.visible');
});
it('first widget should be removed', () => {
cy.get('.mk-widget-edit').click();
cy.get('.customize-container:first-child .remove._button').click();
cy.get('.customize-container').should('have.length', 2);
});
function buildWidgetTest(widgetName) {
it(`${widgetName} widget should get added`, () => {
cy.get('.mk-widget-edit').click();
cy.get('.mk-widget-select select').select(widgetName, { force: true });
cy.get('.bg._modalBg.transparent').click({ multiple: true, force: true });
cy.get('.mk-widget-add').click({ force: true });
cy.get(`.mkw-${widgetName}`).should('exist');
});
}
buildWidgetTest('memo');
buildWidgetTest('notifications');
buildWidgetTest('timeline');
buildWidgetTest('calendar');
buildWidgetTest('rss');
buildWidgetTest('trends');
buildWidgetTest('clock');
buildWidgetTest('activity');
buildWidgetTest('photos');
buildWidgetTest('digitalClock');
buildWidgetTest('federation');
buildWidgetTest('postForm');
buildWidgetTest('slideshow');
buildWidgetTest('serverMetric');
buildWidgetTest('onlineUsers');
buildWidgetTest('jobQueue');
buildWidgetTest('button');
buildWidgetTest('aiscript');
buildWidgetTest('aichan');
});

View File

@ -2,11 +2,11 @@
<div class="vjoppmmu">
<template v-if="edit">
<header>
<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)">
<MkSelect v-model="widgetAdderSelected" style="margin-bottom: var(--margin)" class="mk-widget-select">
<template #label>{{ $ts.selectWidget }}</template>
<option v-for="widget in widgetDefs" :key="widget" :value="widget">{{ $t(`_widgets.${widget}`) }}</option>
</MkSelect>
<MkButton inline primary @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
<MkButton inline primary class="mk-widget-add" @click="addWidget"><i class="fas fa-plus"></i> {{ $ts.add }}</MkButton>
<MkButton inline @click="$emit('exit')">{{ $ts.close }}</MkButton>
</header>
<XDraggable

View File

@ -3,7 +3,7 @@
<XWidgets :edit="editMode" :widgets="defaultStore.reactiveState.widgets.value" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="fas fa-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
<button v-else class="_textButton" style="font-size: 0.9em;" @click="editMode = true"><i class="fas fa-pencil-alt"></i> {{ i18n.ts.editWidgets }}</button>
<button v-else class="_textButton mk-widget-edit" style="font-size: 0.9em;" @click="editMode = true"><i class="fas fa-pencil-alt"></i> {{ i18n.ts.editWidgets }}</button>
</div>
</template>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent">
<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" class="mkw-activity">
<template #header><i class="fas fa-chart-bar"></i>{{ $ts._widgets.activity }}</template>
<template #func><button class="_button" @click="toggleView()"><i class="fas fa-sort"></i></button></template>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :naked="widgetProps.transparent" :show-header="false">
<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-aichan">
<iframe ref="live2d" class="dedjhjmo" src="https://misskey-dev.github.io/mascot-web/?scale=1.5&y=1.1&eyeY=100" @click="touched"></iframe>
</MkContainer>
</template>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader">
<MkContainer :show-header="widgetProps.showHeader" class="mkw-aiscript">
<template #header><i class="fas fa-terminal"></i>{{ $ts._widgets.aiscript }}</template>
<div class="uylguesu _monospace">

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :naked="widgetProps.transparent" :show-header="false">
<MkContainer :naked="widgetProps.transparent" :show-header="false" class="mkw-clock">
<div class="vubelbmv">
<MkAnalogClock class="clock" :thickness="widgetProps.thickness"/>
</div>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader" :foldable="foldable" :scrollable="scrollable">
<MkContainer :show-header="widgetProps.showHeader" :foldable="foldable" :scrollable="scrollable" class="mkw-federation">
<template #header><i class="fas fa-globe"></i>{{ $ts._widgets.federation }}</template>
<div class="wbrkwalb">

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader">
<MkContainer :show-header="widgetProps.showHeader" class="mkw-memo">
<template #header><i class="fas fa-sticky-note"></i>{{ $ts._widgets.memo }}</template>
<div class="otgbylcu">

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :style="`height: ${widgetProps.height}px;`" :show-header="widgetProps.showHeader" :scrollable="true">
<MkContainer :style="`height: ${widgetProps.height}px;`" :show-header="widgetProps.showHeader" :scrollable="true" class="mkw-notifications">
<template #header><i class="fas fa-bell"></i>{{ $ts.notifications }}</template>
<template #func><button class="_button" @click="configureNotification()"><i class="fas fa-cog"></i></button></template>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" :class="$style.root" :data-transparent="widgetProps.transparent ? true : null">
<MkContainer :show-header="widgetProps.showHeader" :naked="widgetProps.transparent" :class="$style.root" :data-transparent="widgetProps.transparent ? true : null" class="mkw-photos">
<template #header><i class="fas fa-camera"></i>{{ $ts._widgets.photos }}</template>
<div class="">

View File

@ -1,5 +1,5 @@
<template>
<XPostForm class="_panel" :fixed="true" :autofocus="false"/>
<XPostForm class="_panel mkw-postForm" :fixed="true" :autofocus="false"/>
</template>
<script lang="ts" setup>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader">
<MkContainer :show-header="widgetProps.showHeader" class="mkw-rss">
<template #header><i class="fas fa-rss-square"></i>RSS</template>
<template #func><button class="_button" @click="configure"><i class="fas fa-cog"></i></button></template>

View File

@ -1,5 +1,5 @@
<template>
<div class="kvausudm _panel" :style="{ height: widgetProps.height + 'px' }">
<div class="kvausudm _panel mkw-slideshow" :style="{ height: widgetProps.height + 'px' }">
<div @click="choose">
<p v-if="widgetProps.folderId == null">
{{ $ts.folder }}

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader" :style="`height: ${widgetProps.height}px;`" :scrollable="true">
<MkContainer :show-header="widgetProps.showHeader" :style="`height: ${widgetProps.height}px;`" :scrollable="true" class="mkw-timeline">
<template #header>
<button class="_button" @click="choose">
<i v-if="widgetProps.src === 'home'" class="fas fa-home"></i>

View File

@ -1,5 +1,5 @@
<template>
<MkContainer :show-header="widgetProps.showHeader">
<MkContainer :show-header="widgetProps.showHeader" class="mkw-trends">
<template #header><i class="fas fa-hashtag"></i>{{ $ts._widgets.trends }}</template>
<div class="wbrkwala">