enhance: e2eテストをできるだけ改良してみた (#8159)
* 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 <Syuilotan@yahoo.co.jp>
			
			
This commit is contained in:
		
							parent
							
								
									cbb7e95d82
								
							
						
					
					
						commit
						e1d69e236f
					
				
					 10 changed files with 99 additions and 71 deletions
				
			
		
							
								
								
									
										39
									
								
								.github/workflows/test.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								.github/workflows/test.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -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 | ||||
|  |  | |||
|  | @ -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(() => { | ||||
|  |  | |||
|  | @ -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; | ||||
| 	} | ||||
| }); | ||||
|  |  | |||
|  | @ -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++) { | ||||
|  |  | |||
|  | @ -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: | ||||
|  |  | |||
|  | @ -32,9 +32,7 @@ const props = defineProps<{ | |||
| const pagingComponent = ref<InstanceType<typeof MkPagination>>(); | ||||
| 
 | ||||
| defineExpose({ | ||||
| 	prepend: (note) => { | ||||
| 		pagingComponent.value?.prepend(note); | ||||
| 	}, | ||||
| 	pagingComponent, | ||||
| }); | ||||
| </script> | ||||
| 
 | ||||
|  |  | |||
|  | @ -25,10 +25,10 @@ const emit = defineEmits<{ | |||
| 
 | ||||
| provide('inChannel', computed(() => props.src === 'channel')); | ||||
| 
 | ||||
| const tlComponent = ref<InstanceType<typeof XNotes>>(); | ||||
| const tlComponent: InstanceType<typeof XNotes> = $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(); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -73,12 +73,11 @@ const queue = ref<Item[]>([]); | |||
| 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<void> => { | ||||
| 	queue.value = []; | ||||
|  | @ -105,9 +104,10 @@ const init = async (): Promise<void> => { | |||
| 			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<void> => { | |||
| }; | ||||
| 
 | ||||
| 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, | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| <template> | ||||
| <div class="_section"> | ||||
| 	<XNotes ref="notes" class="_content" :pagination="pagination"/> | ||||
| 	<XNotes class="_content" :pagination="pagination"/> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ | |||
| 		<option value="replies">{{ $ts.notesAndReplies }}</option> | ||||
| 		<option value="files">{{ $ts.withFiles }}</option> | ||||
| 	</MkTab> | ||||
| 	<XNotes ref="timeline" :no-gap="true" :pagination="pagination"/> | ||||
| 	<XNotes :no-gap="true" :pagination="pagination"/> | ||||
| </div> | ||||
| </template> | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue