feat: Add basic cypress testing
Part-of: <https://dev.funkwhale.audio/funkwhale/funkwhale/-/merge_requests/1795>
This commit is contained in:
parent
cfc167fbf3
commit
9aeefca728
|
@ -313,6 +313,17 @@ build_metadata:
|
||||||
- docker-bake.api.json
|
- docker-bake.api.json
|
||||||
- docker-bake.front.json
|
- docker-bake.front.json
|
||||||
|
|
||||||
|
|
||||||
|
test_integration:
|
||||||
|
interruptible: true
|
||||||
|
stage: test
|
||||||
|
image: cypress/base:16.14.2
|
||||||
|
before_script:
|
||||||
|
- cd front
|
||||||
|
- yarn install
|
||||||
|
script:
|
||||||
|
- yarn run cypress run
|
||||||
|
|
||||||
build_api_schema:
|
build_api_schema:
|
||||||
stage: build
|
stage: build
|
||||||
needs:
|
needs:
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Add basic cypress testing
|
|
@ -22,3 +22,6 @@ yarn-error.log*
|
||||||
|
|
||||||
# Bundle anayzer
|
# Bundle anayzer
|
||||||
stats.html
|
stats.html
|
||||||
|
|
||||||
|
cypress/screenshots
|
||||||
|
cypress/videos
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { defineConfig } from 'cypress'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
chromeWebSecurity: false,
|
||||||
|
e2e: {
|
||||||
|
// We've imported your old cypress plugins here.
|
||||||
|
// You may want to clean this up later by importing these.
|
||||||
|
setupNodeEvents(on, config) {
|
||||||
|
return require('./cypress/plugins/index.js')(on, config)
|
||||||
|
},
|
||||||
|
baseUrl: 'https://demo.funkwhale.audio',
|
||||||
|
},
|
||||||
|
})
|
|
@ -0,0 +1,31 @@
|
||||||
|
describe('Favorites', () => {
|
||||||
|
it('can be done from album list view', () => {
|
||||||
|
cy.login()
|
||||||
|
|
||||||
|
cy.visit('/')
|
||||||
|
cy.wait(4000)
|
||||||
|
|
||||||
|
cy.get('.item.collapse-button-wrapper').click()
|
||||||
|
cy.contains('Albums').click()
|
||||||
|
|
||||||
|
cy.get('.component-album-card').first().within(() => {
|
||||||
|
cy.get('a').first().click()
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.get('.track-row.row').first().trigger('hover').within(() => {
|
||||||
|
cy.get('.favorite-icon').then(($favButton) => {
|
||||||
|
$favButton.click()
|
||||||
|
// In case everything worked the favorite button should be pink
|
||||||
|
cy.wrap($favButton).should('have.class', 'pink')
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.get('.favorite-icon.pink').then(($unfavButton) => {
|
||||||
|
$unfavButton.click()
|
||||||
|
// In case everything worked the favorite button shouldn't be pink
|
||||||
|
// anymore
|
||||||
|
cy.wrap($unfavButton).should('not.have.class', 'pink')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,19 @@
|
||||||
|
describe('The login', () => {
|
||||||
|
it('is working with UI', () => {
|
||||||
|
cy.fixture('testuser.json').then((user) => {
|
||||||
|
cy.visit('/login')
|
||||||
|
cy.get('input[name=username]').type(user['username'])
|
||||||
|
cy.get('input[name=password]').type(`${user['password']}{enter}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
cy.url().should('include', '/library')
|
||||||
|
cy.getCookie('sessionid').should('exist')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('is working without UI', () => {
|
||||||
|
cy.login()
|
||||||
|
cy.visit('/library')
|
||||||
|
cy.get('.ui.avatar.circular.label').should('exist')
|
||||||
|
cy.getCookie('sessionid').should('exist')
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"username": "demo",
|
||||||
|
"password": "testing1234"
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
// ***********************************************************
|
||||||
|
// This example plugins/index.js can be used to load plugins
|
||||||
|
//
|
||||||
|
// You can change the location of this file or turn off loading
|
||||||
|
// the plugins file with the 'pluginsFile' configuration option.
|
||||||
|
//
|
||||||
|
// You can read more here:
|
||||||
|
// https://on.cypress.io/plugins-guide
|
||||||
|
// ***********************************************************
|
||||||
|
|
||||||
|
// This function is called when a project is opened or re-opened (e.g. due to
|
||||||
|
// the project's config changing)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Cypress.PluginConfig}
|
||||||
|
*/
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
module.exports = (on, config) => {
|
||||||
|
// `on` is used to hook into various events Cypress emits
|
||||||
|
// `config` is the resolved Cypress config
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Currently we cannot login purely programmatically, so we need to use the
|
||||||
|
// graphical login until the vue3 branch is merged
|
||||||
|
Cypress.Commands.add('login', () => {
|
||||||
|
cy.fixture('testuser.json').then((user) => {
|
||||||
|
var username = user["username"]
|
||||||
|
var password = user["password"]
|
||||||
|
cy.visit('/login')
|
||||||
|
cy.wait(1000)
|
||||||
|
cy.getCookie('csrftoken').then(($cookie) => {
|
||||||
|
const csrfToken = $cookie?.value
|
||||||
|
|
||||||
|
cy.request({
|
||||||
|
method: 'POST',
|
||||||
|
url: '/api/v1/users/login',
|
||||||
|
form: true,
|
||||||
|
headers: {
|
||||||
|
'X-CSRFTOKEN': csrfToken,
|
||||||
|
Referer: Cypress.config().baseUrl + '/login',
|
||||||
|
},
|
||||||
|
body: {
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,9 @@
|
||||||
|
import './commands'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace Cypress {
|
||||||
|
interface Chainable {
|
||||||
|
login(): Chainable<JQuery<HTMLElement>>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["es5", "dom"],
|
||||||
|
"types": ["cypress", "node"]
|
||||||
|
},
|
||||||
|
"include": ["**/*.ts"],
|
||||||
|
"isolatedModules": false
|
||||||
|
}
|
|
@ -78,6 +78,7 @@
|
||||||
"@vue/test-utils": "2.2.7",
|
"@vue/test-utils": "2.2.7",
|
||||||
"@vue/tsconfig": "0.1.3",
|
"@vue/tsconfig": "0.1.3",
|
||||||
"axios-mock-adapter": "1.21.4",
|
"axios-mock-adapter": "1.21.4",
|
||||||
|
"cypress": "12.13.0",
|
||||||
"eslint": "8.30.0",
|
"eslint": "8.30.0",
|
||||||
"eslint-config-standard": "17.0.0",
|
"eslint-config-standard": "17.0.0",
|
||||||
"eslint-plugin-html": "7.1.0",
|
"eslint-plugin-html": "7.1.0",
|
||||||
|
@ -88,6 +89,7 @@
|
||||||
"eslint-plugin-vue": "9.8.0",
|
"eslint-plugin-vue": "9.8.0",
|
||||||
"jsdom": "20.0.3",
|
"jsdom": "20.0.3",
|
||||||
"jsonc-eslint-parser": "2.1.0",
|
"jsonc-eslint-parser": "2.1.0",
|
||||||
|
"p-limit": "4.0.0",
|
||||||
"rollup-plugin-visualizer": "5.9.0",
|
"rollup-plugin-visualizer": "5.9.0",
|
||||||
"sass": "1.57.1",
|
"sass": "1.57.1",
|
||||||
"sinon": "15.0.2",
|
"sinon": "15.0.2",
|
||||||
|
|
|
@ -25,5 +25,5 @@ const defaultAvatarStyle = computed(() => ({ backgroundColor: `#${actorColor.val
|
||||||
v-else
|
v-else
|
||||||
:style="defaultAvatarStyle"
|
:style="defaultAvatarStyle"
|
||||||
class="ui avatar circular label"
|
class="ui avatar circular label"
|
||||||
>{{ actor.preferred_username[0] }}</span>
|
>{{ actor.preferred_username?.[0] || "" }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { CLIENT_RADIOS } from '~/utils/clientRadios'
|
||||||
|
|
||||||
export const install: InitModule = ({ store }) => {
|
export const install: InitModule = ({ store }) => {
|
||||||
watch(() => store.state.instance.instanceUrl, () => {
|
watch(() => store.state.instance.instanceUrl, () => {
|
||||||
const url = store.getters['instance/absoluteUrl']('api/v1/activity')
|
const url = store.getters['instance/absoluteUrl']('/api/v1/activity')
|
||||||
.replace(/^http/, 'ws')
|
.replace(/^http/, 'ws')
|
||||||
|
|
||||||
const { data, status, open, close } = useWebSocket(url, {
|
const { data, status, open, close } = useWebSocket(url, {
|
||||||
|
|
|
@ -248,12 +248,12 @@ const store: Module<State, RootState> = {
|
||||||
const response = await axios.get('instance/settings/')
|
const response = await axios.get('instance/settings/')
|
||||||
.catch(err => logger.error('Error while fetching settings', err.response.data))
|
.catch(err => logger.error('Error while fetching settings', err.response.data))
|
||||||
|
|
||||||
if (!response) return
|
if (!Array.isArray(response?.data)) return
|
||||||
|
|
||||||
logger.info('Successfully fetched instance settings')
|
logger.info('Successfully fetched instance settings')
|
||||||
|
|
||||||
type SettingsSection = { section: string, name: string }
|
type SettingsSection = { section: string, name: string }
|
||||||
const sections = response.data.reduce((map: Record<string, Record<string, SettingsSection>>, entry: SettingsSection) => {
|
const sections = response?.data.reduce((map: Record<string, Record<string, SettingsSection>>, entry: SettingsSection) => {
|
||||||
map[entry.section] ??= {}
|
map[entry.section] ??= {}
|
||||||
map[entry.section][entry.name] = entry
|
map[entry.section][entry.name] = entry
|
||||||
return map
|
return map
|
||||||
|
|
1984
front/yarn.lock
1984
front/yarn.lock
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue