From 708fba989adab95b3a4c381f6b351979c5973591 Mon Sep 17 00:00:00 2001
From: Andreas Nedbal <github-bf215181b5140522137b3d4f6b73544a@desu.email>
Date: Sat, 28 May 2022 07:28:12 +0200
Subject: [PATCH] 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
---
 cypress/integration/widgets.js                | 84 +++++++++++++++++++
 packages/client/src/components/widgets.vue    |  4 +-
 packages/client/src/ui/universal.widgets.vue  |  2 +-
 packages/client/src/widgets/activity.vue      |  2 +-
 packages/client/src/widgets/aichan.vue        |  2 +-
 packages/client/src/widgets/aiscript.vue      |  2 +-
 packages/client/src/widgets/clock.vue         |  2 +-
 packages/client/src/widgets/federation.vue    |  2 +-
 packages/client/src/widgets/memo.vue          |  2 +-
 packages/client/src/widgets/notifications.vue |  2 +-
 packages/client/src/widgets/photos.vue        |  2 +-
 packages/client/src/widgets/post-form.vue     |  2 +-
 packages/client/src/widgets/rss.vue           |  2 +-
 packages/client/src/widgets/slideshow.vue     |  2 +-
 packages/client/src/widgets/timeline.vue      |  2 +-
 packages/client/src/widgets/trends.vue        |  2 +-
 16 files changed, 100 insertions(+), 16 deletions(-)
 create mode 100644 cypress/integration/widgets.js

diff --git a/cypress/integration/widgets.js b/cypress/integration/widgets.js
new file mode 100644
index 0000000000..d63ff274bd
--- /dev/null
+++ b/cypress/integration/widgets.js
@@ -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');
+});
diff --git a/packages/client/src/components/widgets.vue b/packages/client/src/components/widgets.vue
index b6835795cb..74dd79f733 100644
--- a/packages/client/src/components/widgets.vue
+++ b/packages/client/src/components/widgets.vue
@@ -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
diff --git a/packages/client/src/ui/universal.widgets.vue b/packages/client/src/ui/universal.widgets.vue
index a42c085690..7aed083886 100644
--- a/packages/client/src/ui/universal.widgets.vue
+++ b/packages/client/src/ui/universal.widgets.vue
@@ -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>
 
diff --git a/packages/client/src/widgets/activity.vue b/packages/client/src/widgets/activity.vue
index 631beceb72..7fb9f5894c 100644
--- a/packages/client/src/widgets/activity.vue
+++ b/packages/client/src/widgets/activity.vue
@@ -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>
 
diff --git a/packages/client/src/widgets/aichan.vue b/packages/client/src/widgets/aichan.vue
index 70e47f2af1..cdd367cc84 100644
--- a/packages/client/src/widgets/aichan.vue
+++ b/packages/client/src/widgets/aichan.vue
@@ -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>
diff --git a/packages/client/src/widgets/aiscript.vue b/packages/client/src/widgets/aiscript.vue
index b74e2258a9..9fed292a69 100644
--- a/packages/client/src/widgets/aiscript.vue
+++ b/packages/client/src/widgets/aiscript.vue
@@ -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">
diff --git a/packages/client/src/widgets/clock.vue b/packages/client/src/widgets/clock.vue
index 0a35c4c5ab..fbd2f9e899 100644
--- a/packages/client/src/widgets/clock.vue
+++ b/packages/client/src/widgets/clock.vue
@@ -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>
diff --git a/packages/client/src/widgets/federation.vue b/packages/client/src/widgets/federation.vue
index 1bfb068a2f..a3862077bb 100644
--- a/packages/client/src/widgets/federation.vue
+++ b/packages/client/src/widgets/federation.vue
@@ -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">
diff --git a/packages/client/src/widgets/memo.vue b/packages/client/src/widgets/memo.vue
index f2d1bbc047..8670cb2bac 100644
--- a/packages/client/src/widgets/memo.vue
+++ b/packages/client/src/widgets/memo.vue
@@ -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">
diff --git a/packages/client/src/widgets/notifications.vue b/packages/client/src/widgets/notifications.vue
index f51e983a0e..18c546ee74 100644
--- a/packages/client/src/widgets/notifications.vue
+++ b/packages/client/src/widgets/notifications.vue
@@ -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>
 
diff --git a/packages/client/src/widgets/photos.vue b/packages/client/src/widgets/photos.vue
index 8e30765290..5d9b9e2984 100644
--- a/packages/client/src/widgets/photos.vue
+++ b/packages/client/src/widgets/photos.vue
@@ -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="">
diff --git a/packages/client/src/widgets/post-form.vue b/packages/client/src/widgets/post-form.vue
index 5b74602c85..b542913357 100644
--- a/packages/client/src/widgets/post-form.vue
+++ b/packages/client/src/widgets/post-form.vue
@@ -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>
diff --git a/packages/client/src/widgets/rss.vue b/packages/client/src/widgets/rss.vue
index 6b057cdd06..fc65f11813 100644
--- a/packages/client/src/widgets/rss.vue
+++ b/packages/client/src/widgets/rss.vue
@@ -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>
 
diff --git a/packages/client/src/widgets/slideshow.vue b/packages/client/src/widgets/slideshow.vue
index 1b6c2d766d..fd78edbe40 100644
--- a/packages/client/src/widgets/slideshow.vue
+++ b/packages/client/src/widgets/slideshow.vue
@@ -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 }}
diff --git a/packages/client/src/widgets/timeline.vue b/packages/client/src/widgets/timeline.vue
index c9a9e68bb9..408cf2cbea 100644
--- a/packages/client/src/widgets/timeline.vue
+++ b/packages/client/src/widgets/timeline.vue
@@ -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>
diff --git a/packages/client/src/widgets/trends.vue b/packages/client/src/widgets/trends.vue
index 34bbc16a8b..9680f1c892 100644
--- a/packages/client/src/widgets/trends.vue
+++ b/packages/client/src/widgets/trends.vue
@@ -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">