From 5dcaf58261f486607f14d9fc5ec292a826534dd2 Mon Sep 17 00:00:00 2001
From: snowteamer <64228468+snowteamer@users.noreply.github.com>
Date: Sat, 30 Dec 2023 10:37:54 +0100
Subject: [PATCH 1/3] Fix issue #1789 - wrong timeSince output
---
frontend/model/contracts/shared/time.js | 3 ++-
frontend/model/contracts/shared/time.test.js | 4 ++--
2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/frontend/model/contracts/shared/time.js b/frontend/model/contracts/shared/time.js
index d8674ff9b..9dbce282f 100644
--- a/frontend/model/contracts/shared/time.js
+++ b/frontend/model/contracts/shared/time.js
@@ -224,7 +224,8 @@ export function timeSince (datems: number, dateNow: number = Date.now()): string
if (interval >= DAYS_MILLIS * 2) {
// Make sure to replace any ordinary space character by a non-breaking one.
- return humanDate(datems).replace(/\x32/g, '\xa0')
+ // TODO: use .replaceAll when migrating to TS.
+ return humanDate(datems).replace(/\x20/g, '\xa0')
}
if (interval >= DAYS_MILLIS) {
return L('1d')
diff --git a/frontend/model/contracts/shared/time.test.js b/frontend/model/contracts/shared/time.test.js
index 1349e8caa..d97f36d74 100644
--- a/frontend/model/contracts/shared/time.test.js
+++ b/frontend/model/contracts/shared/time.test.js
@@ -63,10 +63,10 @@ describe('timeSince', function () {
)).equal('1d')
})
- it('should return current day when +48h have passed', () => {
+ it('should return current day (with non-breaking space) when +48h have passed', () => {
should(timeSince(
currentDate - DAYS_MILLIS * 23,
currentDate
- )).equal('May 7')
+ )).equal('May\xa07')
})
})
From c87d97387a4160800dcb3ba038de2eada45410f8 Mon Sep 17 00:00:00 2001
From: snowteamer <64228468+snowteamer@users.noreply.github.com>
Date: Fri, 5 Jan 2024 09:41:35 +0100
Subject: [PATCH 2/3] Use replaceAll with FlowFixMe
---
frontend/model/contracts/shared/time.js | 4 +-
.../integration/agroup-contributions.spec.js | 565 ++++++++++++++++++
2 files changed, 567 insertions(+), 2 deletions(-)
create mode 100644 test/cypress/integration/agroup-contributions.spec.js
diff --git a/frontend/model/contracts/shared/time.js b/frontend/model/contracts/shared/time.js
index 9dbce282f..ec8007549 100644
--- a/frontend/model/contracts/shared/time.js
+++ b/frontend/model/contracts/shared/time.js
@@ -224,8 +224,8 @@ export function timeSince (datems: number, dateNow: number = Date.now()): string
if (interval >= DAYS_MILLIS * 2) {
// Make sure to replace any ordinary space character by a non-breaking one.
- // TODO: use .replaceAll when migrating to TS.
- return humanDate(datems).replace(/\x20/g, '\xa0')
+ // $FlowFixMe
+ return humanDate(datems).replaceAll(' ', '\xa0')
}
if (interval >= DAYS_MILLIS) {
return L('1d')
diff --git a/test/cypress/integration/agroup-contributions.spec.js b/test/cypress/integration/agroup-contributions.spec.js
new file mode 100644
index 000000000..0e09a7929
--- /dev/null
+++ b/test/cypress/integration/agroup-contributions.spec.js
@@ -0,0 +1,565 @@
+const userId = Math.floor(Math.random() * 10000)
+const groupName = 'Dreamers'
+const usersDisplayName = {
+ 1: 'Greg',
+ 2: 'Margarida',
+ 3: 'Pierre',
+ 4: 'Sandrina'
+}
+
+const elReceivingFirst = '.receiving .c-contribution-item:first-child'
+const elGivingFirst = '.giving .c-contribution-item:first-child'
+
+function addNonMonetaryContribution (name) {
+ cy.getByDT('addNonMonetaryContribution', 'button').click()
+ cy.getByDT('inputNonMonetaryContribution').type(name)
+ cy.getByDT('buttonAddNonMonetaryContribution', 'button').click()
+ cy.getByDT('buttonAddNonMonetaryContribution', 'button').should('not.exist')
+
+ // Assert the contribution was added to the list once
+ cy.getByDT('givingList').should($list => {
+ const contribution = $list.find('li').filter((i, item) => {
+ return item.textContent.includes(name) && item.getAttribute('data-test') === 'editable'
+ })
+ expect(contribution).to.have.length(1)
+ })
+}
+
+function assertNonMonetaryEditableValue (name) {
+ cy.getByDT('buttonEditNonMonetaryContribution').click()
+ cy.getByDT('inputNonMonetaryContribution').should('have.value', name)
+ cy.getByDT('buttonSaveNonMonetaryContribution').click()
+}
+
+function assertGraphicSummary (legendListItems) {
+ cy.getByDT('groupPledgeSummary', 'ul').within(([list]) => {
+ legendListItems.forEach((legendText, index) => {
+ cy.get(list).children().eq(index)
+ .invoke('text')
+ .should('contain', legendText)
+ })
+ })
+}
+
+function assertContributionsWidget (assertions) {
+ cy.getByDT('dashboard', 'a').click()
+ cy.getByDT('contributionsWidget').within(() => {
+ Object.keys(assertions).forEach(dataTest => {
+ cy.getByDT(dataTest).should('contain', assertions[dataTest])
+ })
+ })
+ cy.getByDT('contributionsLink', 'a').click()
+}
+
+function updateIncome (newIncome, needsIncome, graphicLegend, incomeStatus) {
+ cy.getByDT('contributionsLink').click()
+ cy.getByDT('openIncomeDetailsModal').click()
+ cy.getByDT(needsIncome ? 'needsIncomeRadio' : 'doesntNeedIncomeRadio').click()
+ cy.getByDT('inputIncomeOrPledge').clear().type(newIncome)
+
+ assertGraphicSummary(graphicLegend)
+
+ if (needsIncome) {
+ // entering the payment details is mandatory for 'needsIncome'
+ cy.randomPaymentMethodInIncomeDetails()
+ }
+
+ cy.getByDT('submitIncome').click()
+ cy.getByDT('closeModal').should('not.exist') // make sure the modal closes.
+
+ const elIncomeStatus = needsIncome ? elReceivingFirst : elGivingFirst
+ cy.get(elIncomeStatus).should('contain', incomeStatus)
+}
+
+describe('Contributions', () => {
+ const invitationLinks = {}
+
+ it('user1 creates a group', () => {
+ cy.visit('/')
+ cy.giSignup(`user1-${userId}`, { bypassUI: true })
+
+ cy.giCreateGroup(groupName, { bypassUI: true })
+
+ cy.giSetDisplayName(usersDisplayName[1])
+
+ cy.giGetInvitationAnyone().then(url => {
+ invitationLinks.anyone = url
+ })
+
+ cy.giLogout()
+ })
+
+ it('user2, user3 and user4 join the group', () => {
+ for (let i = 2; i <= 4; i++) {
+ cy.giAcceptGroupInvite(invitationLinks.anyone, {
+ username: `user${i}-${userId}`,
+ groupName,
+ displayName: usersDisplayName[i],
+ bypassUI: true
+ })
+ }
+
+ cy.giLogin(`user1-${userId}`, { bypassUI: true })
+ })
+
+ it('user1 fills their Income Details - pledges $500', () => {
+ cy.getByDT('contributionsLink').click()
+ cy.getByDT('addIncomeDetailsCard').should('contain', 'Add your income details')
+
+ cy.getByDT('openIncomeDetailsModal').click()
+ // Make sure only radio box to select the type is visible at the begining
+ cy.getByDT('introIncomeOrPledge').should('not.exist')
+
+ cy.getByDT('doesntNeedIncomeRadio').click()
+ // Make sure the user is aksed how much he want to pledge
+ cy.getByDT('introIncomeOrPledge').should('contain', 'How much do you want to pledge?')
+
+ assertGraphicSummary([
+ 'Total Pledged$0',
+ 'Needed Pledges$0'
+ ])
+
+ // Users should be allowed to pledge 0 (see #1027).
+ cy.getByDT('inputIncomeOrPledge').type('0')
+ cy.getByDT('badIncome').should('not.be.visible')
+
+ // Users should not be allowed to pledge a negative amount.
+ cy.getByDT('inputIncomeOrPledge').clear().type('-50')
+ cy.getByDT('badIncome').should('be.visible')
+ .and('contain', 'Oops, you entered a negative number')
+
+ assertGraphicSummary([
+ 'Total Pledged$0',
+ 'Needed Pledges$0'
+ ])
+
+ cy.getByDT('inputIncomeOrPledge').clear().type(500)
+
+ assertGraphicSummary([
+ 'Total Pledged$500',
+ 'Needed Pledges$0'
+ ])
+
+ cy.getByDT('submitIncome').click()
+ // After selecting the amount and close the modal make sure it show that no one is in need
+ cy.getByDT('receivingParagraph').should('contain', 'When other members pledge a monetary or non-monetary contribution, they will appear here.')
+ cy.getByDT('givingParagraph').should('contain', 'No one needs monetary contributions at the moment. You can still add non-monetary contributions if you would like.')
+
+ assertContributionsWidget({
+ paymentsTitle: 'Payments sent',
+ paymentsStatus: 'At the moment, no one is in need of contributions.',
+ monetaryTitle: 'You are pledging $500',
+ monetaryStatus: '$0 will be used.',
+ nonMonetaryStatus: 'There are no non-monetary contributions.'
+ })
+ })
+
+ it('user1 decides to switch income details to needing $100 and add a payment info', () => {
+ cy.getByDT('openIncomeDetailsModal').click()
+ cy.getByDT('needsIncomeRadio').click()
+ // After swithing to need income, it should ask user how much he need
+ cy.getByDT('introIncomeOrPledge').should('contain', 'What\'s your monthly income?')
+ cy.getByDT('inputIncomeOrPledge').type(500)
+ // It should not let user ask for money if he has more than the basic income
+ cy.getByDT('badIncome').should('contain', 'Your income must be lower than the group mincome')
+ cy.getByDT('inputIncomeOrPledge').clear().type(100)
+ // After updating the income under the limit it should hide the error message
+ cy.getByDT('badIncome').should('not.be.visible')
+
+ assertGraphicSummary([
+ 'Total Pledged$0',
+ 'Needed Pledges$100'
+ ])
+
+ cy.getByDT('submitIncome').click()
+ // When 'need income' is selected, payment details is requried.
+ cy.getByDT('feedbackMsg').should('contain', 'Payment details required. Please let people know how they can pay you.')
+
+ // Fill out the payment details (bitcoin)
+ cy.getByDT('paymentMethods').within(() => {
+ cy.getByDT('fields', 'ul').children().should('have.length', 1)
+
+ cy.log('Fill the 1º payment method (bitcoin)')
+ cy.getByDT('method').within(() => {
+ cy.getByDT('remove', 'button').should('not.be.visible')
+ cy.get('select')
+ .should('have.value', null)
+ .select('bitcoin')
+ cy.get('input').type('h4sh-t0-b3-s4ved')
+ cy.getByDT('remove', 'button').should('be.visible')
+ })
+ })
+
+ cy.getByDT('submitIncome').click()
+ cy.getByDT('closeModal').should('not.exist')
+
+ // After closing the modal it should dislay how much user need
+ cy.getByDT('headerNeed').should('contain', 'You need $100')
+ // The user should be inform that even if he can't pledge he can still contribute
+ cy.getByDT('givingParagraph').should('contain', 'You can contribute to your group with money or other valuables like teaching skills, sharing your time to help someone. The sky is the limit!')
+
+ assertContributionsWidget({
+ paymentsTitle: 'Payments received',
+ paymentsStatus: 'No members in the group are pledging yet! 😔',
+ monetaryTitle: 'You need $100',
+ monetaryStatus: 'You will receive $0.',
+ nonMonetaryStatus: 'There are no non-monetary contributions.'
+ })
+ })
+
+ it('user1 adds additional payment info', () => {
+ cy.getByDT('openIncomeDetailsModal').click()
+
+ cy.getByDT('paymentMethods').within(() => {
+ cy.log('Add a 2º payment method (paypal)')
+ cy.getByDT('addMethod', 'button').click()
+ cy.getByDT('fields', 'ul').children().should('have.length', 2)
+
+ cy.getByDT('method').eq(1).within(() => {
+ cy.getByDT('remove', 'button').should('be.visible')
+ cy.get('select').should('have.value', null)
+ cy.get('input').should('have.value', '')
+ cy.get('select').select('paypal')
+ cy.get('input').type('user1-paypal@email.com')
+ })
+
+ cy.log('Add a 3º payment method (other)')
+ cy.getByDT('addMethod', 'button').click()
+ cy.getByDT('fields', 'ul').children().should('have.length', 3)
+
+ cy.getByDT('method').eq(2).within(() => {
+ cy.get('select').should('have.value', null)
+ cy.get('input').should('have.value', '')
+ cy.get('select').select('other')
+ cy.get('input').type('IBAN: 12345')
+ cy.getByDT('remove', 'button').should('be.visible')
+ })
+
+ cy.log('Remove the 2º payment method (paypal)')
+ cy.getByDT('method').eq(1).within(() => {
+ cy.getByDT('remove', 'button').click()
+ })
+
+ cy.getByDT('fields', 'ul').children().should('have.length', 2)
+
+ cy.log('Add a 3º same payment method (other)')
+ cy.getByDT('addMethod', 'button').click()
+ cy.getByDT('method').eq(2).within(() => {
+ cy.get('select').should('have.value', null)
+ cy.get('input').should('have.value', '')
+ cy.get('select').select('other')
+ cy.get('input').type('MBWAY: 91 2345678')
+ cy.getByDT('remove', 'button').should('be.visible')
+ })
+ })
+
+ cy.getByDT('submitIncome').click()
+ cy.getByDT('closeModal').should('not.exist')
+
+ cy.log('Verify saved payment info (bitcoin and 2 other)')
+ cy.getByDT('openIncomeDetailsModal').click()
+ // HACK FOR A BIZARRE HEISENBUGG!!!
+ // Description: without this, sometimes the payment methods do not appear
+ // in the list for some reason, but they re-appear if we close and open the modal
+ cy.closeModal()
+ cy.getByDT('openIncomeDetailsModal').click()
+ // HACK FOR A BIZARRE HEISENBUGG!!!
+ cy.getByDT('paymentMethods').within(() => {
+ cy.getByDT('fields', 'ul').children().should('have.length', 3)
+ cy.getByDT('method').eq(0).within(() => {
+ cy.get('select').should('have.value', 'bitcoin')
+ cy.get('input').should('have.value', 'h4sh-t0-b3-s4ved')
+ cy.getByDT('remove', 'button').should('be.visible')
+ })
+ cy.getByDT('method').eq(1).within(() => {
+ cy.get('select').should('have.value', 'other')
+ cy.get('input').should('have.value', 'IBAN: 12345')
+ cy.getByDT('remove', 'button').should('be.visible')
+ })
+ cy.getByDT('method').eq(2).within(() => {
+ cy.get('select').should('have.value', 'other')
+ cy.get('input').should('have.value', 'MBWAY: 91 2345678')
+ cy.getByDT('remove', 'button').should('be.visible')
+ })
+
+ cy.log('Try to add a 4º payment method - incompleted !name')
+ cy.getByDT('addMethod', 'button').click()
+ cy.getByDT('method').eq(3).within(() => {
+ cy.get('input').type('mylink.com')
+ })
+ })
+
+ cy.getByDT('submitIncome').click()
+ cy.getByDT('feedbackMsg').should('contain', 'The method name for "mylink.com" is missing.')
+
+ cy.getByDT('paymentMethods').within(() => {
+ // Remove the previous incomplete method
+ cy.getByDT('method').eq(3).within(() => {
+ cy.getByDT('remove', 'button').click()
+ })
+
+ cy.log('Try to add a 4º payment method - incompleted !value')
+ // Add a new method... incompleted (no value)
+ cy.getByDT('addMethod', 'button').click()
+ cy.getByDT('method').eq(3).within(() => {
+ cy.get('select').select('paypal')
+ })
+ })
+
+ cy.getByDT('submitIncome').click()
+ cy.getByDT('feedbackMsg').should('contain', 'The method "paypal" is incomplete.')
+
+ cy.closeModal()
+ })
+
+ it('user1 have their payment info on the profile card', () => {
+ cy.getByDT('openProfileCard').click()
+
+ cy.getByDT('profilePaymentMethods').within(() => {
+ cy.get('ul').children().should('have.length', 3)
+ cy.getByDT('profilePaymentMethod').eq(0).within(() => {
+ cy.get('span').eq(0).should('contain', 'bitcoin')
+ cy.get('span').eq(1).should('contain', 'h4sh-t0-b3-s4ved')
+ })
+ cy.getByDT('profilePaymentMethod').eq(1).within(() => {
+ cy.get('span').eq(0).should('contain', 'other')
+ cy.get('span').eq(1).should('contain', 'IBAN: 12345')
+ })
+ cy.getByDT('profilePaymentMethod').eq(2).within(() => {
+ cy.get('span').eq(0).should('contain', 'other')
+ cy.get('span').eq(1).should('contain', 'MBWAY: 91 2345678')
+ })
+ })
+ cy.getByDT('closeProfileCard').click()
+ })
+
+ const firstContribution = 'Portuguese classes'
+
+ it('user1 adds non monetary contribution', () => {
+ addNonMonetaryContribution(firstContribution)
+
+ cy.getByDT('givingList', 'ul')
+ .get('li.is-editable')
+ .should('have.length', 1)
+ .should('contain', firstContribution)
+ })
+
+ it('user1 removes non monetary contribution', () => {
+ cy.getByDT('buttonEditNonMonetaryContribution')
+
+ cy.getByDT('givingList').find('li').should('have.length', 2) // contribution + cta to add
+ cy.getByDT('buttonEditNonMonetaryContribution').click()
+ cy.getByDT('buttonRemoveNonMonetaryContribution').click()
+ cy.getByDT('givingList').find('li').should('have.length', 1) // cta to add
+ cy.getByDT('givingParagraph').should('exist')
+ })
+
+ it('user1 re-adds the same non monetary contribution', () => {
+ addNonMonetaryContribution(firstContribution)
+ cy.getByDT('givingList', 'ul')
+ .get('li.is-editable')
+ .should('have.length', 1)
+ .should('contain', firstContribution)
+ })
+
+ it('user1 edits the non monetary contribution', () => {
+ cy.getByDT('buttonEditNonMonetaryContribution').click()
+ cy.getByDT('inputNonMonetaryContribution').clear().type('French classes{enter}')
+ assertNonMonetaryEditableValue('French classes')
+ // Double check // TODO - Why do we need this?
+ assertNonMonetaryEditableValue('French classes')
+
+ cy.getByDT('givingList', 'ul')
+ .get('li.is-editable')
+ .should('have.length', 1)
+ .should('contain', 'French classes')
+ })
+
+ it('user1 edits it again but cancel it', () => {
+ cy.getByDT('buttonEditNonMonetaryContribution').click()
+ cy.getByDT('buttonCancelNonMonetaryContribution').click()
+ cy.getByDT('givingList', 'ul')
+ .get('li.is-editable')
+ .should('have.length', 1)
+ .should('contain', 'French classes')
+ })
+
+ it('user1 adds 3 more non monetary contributions', () => {
+ addNonMonetaryContribution('German classes')
+ addNonMonetaryContribution('Russian classes')
+ addNonMonetaryContribution('Korean classes')
+
+ cy.getByDT('givingList', 'ul')
+ .get('li.is-editable')
+ .should('have.length', 4)
+
+ assertContributionsWidget({
+ nonMonetaryStatus: 'You are contributing.'
+ })
+ })
+
+ it('user1 have their payment info on the member list profile card', () => {
+ cy.getByDT('dashboard', 'a').click()
+ cy.getByDT('openMemberProfileCard').eq(0).click()
+
+ cy.log('The first member card should not contain payment info')
+ cy.getByDT('profilePaymentMethods').should('not.exist')
+ cy.getByDT('closeProfileCard').click()
+
+ cy.log('The last member card should contain payments info')
+ cy.getByDT('openMemberProfileCard').eq(3).click()
+ cy.getByDT('profilePaymentMethods').within(() => {
+ cy.get('ul').children().should('have.length', 3)
+ cy.getByDT('profilePaymentMethod').eq(0).within(() => {
+ cy.get('span').eq(0).should('contain', 'bitcoin')
+ cy.get('span').eq(1).should('contain', 'h4sh-t0-b3-s4ved')
+ })
+ cy.getByDT('profilePaymentMethod').eq(1).within(() => {
+ cy.get('span').eq(0).should('contain', 'other')
+ cy.get('span').eq(1).should('contain', 'IBAN: 12345')
+ })
+ cy.getByDT('profilePaymentMethod').eq(2).within(() => {
+ cy.get('span').eq(0).should('contain', 'other')
+ cy.get('span').eq(1).should('contain', 'MBWAY: 91 2345678')
+ })
+ })
+
+ cy.getByDT('closeProfileCard').click(('topLeft'))
+ })
+
+ it('user2 pledges $100 and sees their contributions.', () => {
+ cy.giSwitchUser(`user2-${userId}`)
+
+ const graphicLegend = [
+ 'Total Pledged$100',
+ 'Needed Pledges$0'
+ ]
+ updateIncome(100, false, graphicLegend, '$100 to Greg')
+
+ cy.get(elReceivingFirst)
+ .should('contain', 'French classes from Greg')
+
+ cy.get('.receiving .c-contribution-list')
+ .should('have.length', 4)
+ })
+
+ it('user2 adds 2 non monetary contribution', () => {
+ addNonMonetaryContribution('Korean classes')
+ addNonMonetaryContribution('French classes')
+
+ cy.get('.giving .c-contribution-list')
+ .should('have.length', 3)
+
+ assertContributionsWidget({
+ paymentsSummary: ' ', // TODO - just confirm it exists for now.
+ monetaryTitle: 'You are pledging $100',
+ monetaryStatus: '$100 will be used.',
+ nonMonetaryStatus: 'You and 1 other members are contributing.'
+ })
+ })
+
+ it('user3 pledges $100 and sees who they are pledging to - $50 to user1 (Greg)', () => {
+ cy.giSwitchUser(`user3-${userId}`)
+ const graphicLegend = [
+ 'Total Pledged$200',
+ 'Needed Pledges$0'
+ ]
+ updateIncome(100, false, graphicLegend, '$50 to Greg')
+ })
+
+ it('user4 and user2 increase their pledges to $500 each. user1 sees the receiving contributions from 3 members.', () => {
+ cy.giSwitchUser(`user4-${userId}`)
+ const graphicLegend4 = [
+ 'Total Pledged$700',
+ 'Needed Pledges$0',
+ 'Surplus$600'
+ ]
+ updateIncome(500, false, graphicLegend4, '$71.43 to Greg')
+ addNonMonetaryContribution('Korean classes')
+
+ cy.giSwitchUser(`user2-${userId}`)
+ const graphicLegend2 = [
+ 'Total Pledged$1100',
+ 'Needed Pledges$0',
+ 'Surplus$1000'
+ ]
+ updateIncome(500, false, graphicLegend2, '$45.45 to Greg')
+
+ cy.giSwitchUser(`user1-${userId}`)
+
+ cy.getByDT('contributionsLink').click()
+ cy.get(elReceivingFirst).should('contain', '$100 from 3 members')
+
+ assertContributionsWidget({
+ paymentsSummary: ' ', // TODO - just confirm it exists for now.
+ monetaryTitle: 'You need $100',
+ monetaryStatus: 'You will receive $100.',
+ nonMonetaryStatus: 'You and 2 other members are contributing.'
+ })
+ })
+
+ it('user4 and user2 reduced income to $10 and now receive money.', () => {
+ cy.giSwitchUser(`user4-${userId}`)
+ const graphicLegend4 = [
+ 'Total Pledged$600',
+ 'Needed Pledges$0',
+ 'Surplus$310',
+ "You'll receive$190"
+ ]
+ updateIncome(10, true, graphicLegend4, '$190 from Margarida and Pierre')
+
+ cy.giSwitchUser(`user2-${userId}`)
+ const graphicLegend2 = [
+ 'Total Pledged$100',
+ 'Needed Pledges$380',
+ "You'll receive$39.58"
+ ]
+ updateIncome(10, true, graphicLegend2, '$39.58 from Pierre')
+
+ assertContributionsWidget({
+ paymentsSummary: ' ', // TODO - just confirm it exists for now.
+ monetaryTitle: 'You need $190',
+ monetaryStatus: 'You will receive $39.58.',
+ nonMonetaryStatus: 'You and 2 other members are contributing.'
+ })
+ })
+
+ it('user3 pledges to all 3 members', () => {
+ cy.giSwitchUser(`user3-${userId}`)
+ cy.getByDT('contributionsLink').click()
+
+ cy.get(elGivingFirst)
+ .should('contain', 'A total of $100 to 3 members')
+ })
+
+ it('user1 receives part of what they need', () => {
+ cy.giSwitchUser(`user1-${userId}`)
+ cy.getByDT('contributionsLink').click()
+
+ cy.get(elReceivingFirst)
+ .should('contain', '$20.83 from Pierre')
+
+ assertContributionsWidget({
+ paymentsSummary: ' ', // TODO - just confirm it exists for now.
+ monetaryTitle: 'You need $100',
+ monetaryStatus: 'You will receive $20.83.',
+ nonMonetaryStatus: 'You and 2 other members are contributing.'
+ })
+ cy.giLogout()
+ })
+})
+
+/*
+Summary of the group status so far:
+user1
+ - needs $100
+ - $20.83 from pierre
+user2
+ - needs $190
+ - $39.58 from pierre
+user3
+ - pledges $100 to user1, user2 and user4
+user4
+ - needs $190
+ - $39.58 from pierre
+*/
From ffe04c1e6ccb73f90eb1ffa7888015e173002a6e Mon Sep 17 00:00:00 2001
From: snowteamer <64228468+snowteamer@users.noreply.github.com>
Date: Thu, 11 Jan 2024 18:23:49 +0100
Subject: [PATCH 3/3] Remove renamed test file
---
.../integration/agroup-contributions.spec.js | 565 ------------------
1 file changed, 565 deletions(-)
delete mode 100644 test/cypress/integration/agroup-contributions.spec.js
diff --git a/test/cypress/integration/agroup-contributions.spec.js b/test/cypress/integration/agroup-contributions.spec.js
deleted file mode 100644
index 0e09a7929..000000000
--- a/test/cypress/integration/agroup-contributions.spec.js
+++ /dev/null
@@ -1,565 +0,0 @@
-const userId = Math.floor(Math.random() * 10000)
-const groupName = 'Dreamers'
-const usersDisplayName = {
- 1: 'Greg',
- 2: 'Margarida',
- 3: 'Pierre',
- 4: 'Sandrina'
-}
-
-const elReceivingFirst = '.receiving .c-contribution-item:first-child'
-const elGivingFirst = '.giving .c-contribution-item:first-child'
-
-function addNonMonetaryContribution (name) {
- cy.getByDT('addNonMonetaryContribution', 'button').click()
- cy.getByDT('inputNonMonetaryContribution').type(name)
- cy.getByDT('buttonAddNonMonetaryContribution', 'button').click()
- cy.getByDT('buttonAddNonMonetaryContribution', 'button').should('not.exist')
-
- // Assert the contribution was added to the list once
- cy.getByDT('givingList').should($list => {
- const contribution = $list.find('li').filter((i, item) => {
- return item.textContent.includes(name) && item.getAttribute('data-test') === 'editable'
- })
- expect(contribution).to.have.length(1)
- })
-}
-
-function assertNonMonetaryEditableValue (name) {
- cy.getByDT('buttonEditNonMonetaryContribution').click()
- cy.getByDT('inputNonMonetaryContribution').should('have.value', name)
- cy.getByDT('buttonSaveNonMonetaryContribution').click()
-}
-
-function assertGraphicSummary (legendListItems) {
- cy.getByDT('groupPledgeSummary', 'ul').within(([list]) => {
- legendListItems.forEach((legendText, index) => {
- cy.get(list).children().eq(index)
- .invoke('text')
- .should('contain', legendText)
- })
- })
-}
-
-function assertContributionsWidget (assertions) {
- cy.getByDT('dashboard', 'a').click()
- cy.getByDT('contributionsWidget').within(() => {
- Object.keys(assertions).forEach(dataTest => {
- cy.getByDT(dataTest).should('contain', assertions[dataTest])
- })
- })
- cy.getByDT('contributionsLink', 'a').click()
-}
-
-function updateIncome (newIncome, needsIncome, graphicLegend, incomeStatus) {
- cy.getByDT('contributionsLink').click()
- cy.getByDT('openIncomeDetailsModal').click()
- cy.getByDT(needsIncome ? 'needsIncomeRadio' : 'doesntNeedIncomeRadio').click()
- cy.getByDT('inputIncomeOrPledge').clear().type(newIncome)
-
- assertGraphicSummary(graphicLegend)
-
- if (needsIncome) {
- // entering the payment details is mandatory for 'needsIncome'
- cy.randomPaymentMethodInIncomeDetails()
- }
-
- cy.getByDT('submitIncome').click()
- cy.getByDT('closeModal').should('not.exist') // make sure the modal closes.
-
- const elIncomeStatus = needsIncome ? elReceivingFirst : elGivingFirst
- cy.get(elIncomeStatus).should('contain', incomeStatus)
-}
-
-describe('Contributions', () => {
- const invitationLinks = {}
-
- it('user1 creates a group', () => {
- cy.visit('/')
- cy.giSignup(`user1-${userId}`, { bypassUI: true })
-
- cy.giCreateGroup(groupName, { bypassUI: true })
-
- cy.giSetDisplayName(usersDisplayName[1])
-
- cy.giGetInvitationAnyone().then(url => {
- invitationLinks.anyone = url
- })
-
- cy.giLogout()
- })
-
- it('user2, user3 and user4 join the group', () => {
- for (let i = 2; i <= 4; i++) {
- cy.giAcceptGroupInvite(invitationLinks.anyone, {
- username: `user${i}-${userId}`,
- groupName,
- displayName: usersDisplayName[i],
- bypassUI: true
- })
- }
-
- cy.giLogin(`user1-${userId}`, { bypassUI: true })
- })
-
- it('user1 fills their Income Details - pledges $500', () => {
- cy.getByDT('contributionsLink').click()
- cy.getByDT('addIncomeDetailsCard').should('contain', 'Add your income details')
-
- cy.getByDT('openIncomeDetailsModal').click()
- // Make sure only radio box to select the type is visible at the begining
- cy.getByDT('introIncomeOrPledge').should('not.exist')
-
- cy.getByDT('doesntNeedIncomeRadio').click()
- // Make sure the user is aksed how much he want to pledge
- cy.getByDT('introIncomeOrPledge').should('contain', 'How much do you want to pledge?')
-
- assertGraphicSummary([
- 'Total Pledged$0',
- 'Needed Pledges$0'
- ])
-
- // Users should be allowed to pledge 0 (see #1027).
- cy.getByDT('inputIncomeOrPledge').type('0')
- cy.getByDT('badIncome').should('not.be.visible')
-
- // Users should not be allowed to pledge a negative amount.
- cy.getByDT('inputIncomeOrPledge').clear().type('-50')
- cy.getByDT('badIncome').should('be.visible')
- .and('contain', 'Oops, you entered a negative number')
-
- assertGraphicSummary([
- 'Total Pledged$0',
- 'Needed Pledges$0'
- ])
-
- cy.getByDT('inputIncomeOrPledge').clear().type(500)
-
- assertGraphicSummary([
- 'Total Pledged$500',
- 'Needed Pledges$0'
- ])
-
- cy.getByDT('submitIncome').click()
- // After selecting the amount and close the modal make sure it show that no one is in need
- cy.getByDT('receivingParagraph').should('contain', 'When other members pledge a monetary or non-monetary contribution, they will appear here.')
- cy.getByDT('givingParagraph').should('contain', 'No one needs monetary contributions at the moment. You can still add non-monetary contributions if you would like.')
-
- assertContributionsWidget({
- paymentsTitle: 'Payments sent',
- paymentsStatus: 'At the moment, no one is in need of contributions.',
- monetaryTitle: 'You are pledging $500',
- monetaryStatus: '$0 will be used.',
- nonMonetaryStatus: 'There are no non-monetary contributions.'
- })
- })
-
- it('user1 decides to switch income details to needing $100 and add a payment info', () => {
- cy.getByDT('openIncomeDetailsModal').click()
- cy.getByDT('needsIncomeRadio').click()
- // After swithing to need income, it should ask user how much he need
- cy.getByDT('introIncomeOrPledge').should('contain', 'What\'s your monthly income?')
- cy.getByDT('inputIncomeOrPledge').type(500)
- // It should not let user ask for money if he has more than the basic income
- cy.getByDT('badIncome').should('contain', 'Your income must be lower than the group mincome')
- cy.getByDT('inputIncomeOrPledge').clear().type(100)
- // After updating the income under the limit it should hide the error message
- cy.getByDT('badIncome').should('not.be.visible')
-
- assertGraphicSummary([
- 'Total Pledged$0',
- 'Needed Pledges$100'
- ])
-
- cy.getByDT('submitIncome').click()
- // When 'need income' is selected, payment details is requried.
- cy.getByDT('feedbackMsg').should('contain', 'Payment details required. Please let people know how they can pay you.')
-
- // Fill out the payment details (bitcoin)
- cy.getByDT('paymentMethods').within(() => {
- cy.getByDT('fields', 'ul').children().should('have.length', 1)
-
- cy.log('Fill the 1º payment method (bitcoin)')
- cy.getByDT('method').within(() => {
- cy.getByDT('remove', 'button').should('not.be.visible')
- cy.get('select')
- .should('have.value', null)
- .select('bitcoin')
- cy.get('input').type('h4sh-t0-b3-s4ved')
- cy.getByDT('remove', 'button').should('be.visible')
- })
- })
-
- cy.getByDT('submitIncome').click()
- cy.getByDT('closeModal').should('not.exist')
-
- // After closing the modal it should dislay how much user need
- cy.getByDT('headerNeed').should('contain', 'You need $100')
- // The user should be inform that even if he can't pledge he can still contribute
- cy.getByDT('givingParagraph').should('contain', 'You can contribute to your group with money or other valuables like teaching skills, sharing your time to help someone. The sky is the limit!')
-
- assertContributionsWidget({
- paymentsTitle: 'Payments received',
- paymentsStatus: 'No members in the group are pledging yet! 😔',
- monetaryTitle: 'You need $100',
- monetaryStatus: 'You will receive $0.',
- nonMonetaryStatus: 'There are no non-monetary contributions.'
- })
- })
-
- it('user1 adds additional payment info', () => {
- cy.getByDT('openIncomeDetailsModal').click()
-
- cy.getByDT('paymentMethods').within(() => {
- cy.log('Add a 2º payment method (paypal)')
- cy.getByDT('addMethod', 'button').click()
- cy.getByDT('fields', 'ul').children().should('have.length', 2)
-
- cy.getByDT('method').eq(1).within(() => {
- cy.getByDT('remove', 'button').should('be.visible')
- cy.get('select').should('have.value', null)
- cy.get('input').should('have.value', '')
- cy.get('select').select('paypal')
- cy.get('input').type('user1-paypal@email.com')
- })
-
- cy.log('Add a 3º payment method (other)')
- cy.getByDT('addMethod', 'button').click()
- cy.getByDT('fields', 'ul').children().should('have.length', 3)
-
- cy.getByDT('method').eq(2).within(() => {
- cy.get('select').should('have.value', null)
- cy.get('input').should('have.value', '')
- cy.get('select').select('other')
- cy.get('input').type('IBAN: 12345')
- cy.getByDT('remove', 'button').should('be.visible')
- })
-
- cy.log('Remove the 2º payment method (paypal)')
- cy.getByDT('method').eq(1).within(() => {
- cy.getByDT('remove', 'button').click()
- })
-
- cy.getByDT('fields', 'ul').children().should('have.length', 2)
-
- cy.log('Add a 3º same payment method (other)')
- cy.getByDT('addMethod', 'button').click()
- cy.getByDT('method').eq(2).within(() => {
- cy.get('select').should('have.value', null)
- cy.get('input').should('have.value', '')
- cy.get('select').select('other')
- cy.get('input').type('MBWAY: 91 2345678')
- cy.getByDT('remove', 'button').should('be.visible')
- })
- })
-
- cy.getByDT('submitIncome').click()
- cy.getByDT('closeModal').should('not.exist')
-
- cy.log('Verify saved payment info (bitcoin and 2 other)')
- cy.getByDT('openIncomeDetailsModal').click()
- // HACK FOR A BIZARRE HEISENBUGG!!!
- // Description: without this, sometimes the payment methods do not appear
- // in the list for some reason, but they re-appear if we close and open the modal
- cy.closeModal()
- cy.getByDT('openIncomeDetailsModal').click()
- // HACK FOR A BIZARRE HEISENBUGG!!!
- cy.getByDT('paymentMethods').within(() => {
- cy.getByDT('fields', 'ul').children().should('have.length', 3)
- cy.getByDT('method').eq(0).within(() => {
- cy.get('select').should('have.value', 'bitcoin')
- cy.get('input').should('have.value', 'h4sh-t0-b3-s4ved')
- cy.getByDT('remove', 'button').should('be.visible')
- })
- cy.getByDT('method').eq(1).within(() => {
- cy.get('select').should('have.value', 'other')
- cy.get('input').should('have.value', 'IBAN: 12345')
- cy.getByDT('remove', 'button').should('be.visible')
- })
- cy.getByDT('method').eq(2).within(() => {
- cy.get('select').should('have.value', 'other')
- cy.get('input').should('have.value', 'MBWAY: 91 2345678')
- cy.getByDT('remove', 'button').should('be.visible')
- })
-
- cy.log('Try to add a 4º payment method - incompleted !name')
- cy.getByDT('addMethod', 'button').click()
- cy.getByDT('method').eq(3).within(() => {
- cy.get('input').type('mylink.com')
- })
- })
-
- cy.getByDT('submitIncome').click()
- cy.getByDT('feedbackMsg').should('contain', 'The method name for "mylink.com" is missing.')
-
- cy.getByDT('paymentMethods').within(() => {
- // Remove the previous incomplete method
- cy.getByDT('method').eq(3).within(() => {
- cy.getByDT('remove', 'button').click()
- })
-
- cy.log('Try to add a 4º payment method - incompleted !value')
- // Add a new method... incompleted (no value)
- cy.getByDT('addMethod', 'button').click()
- cy.getByDT('method').eq(3).within(() => {
- cy.get('select').select('paypal')
- })
- })
-
- cy.getByDT('submitIncome').click()
- cy.getByDT('feedbackMsg').should('contain', 'The method "paypal" is incomplete.')
-
- cy.closeModal()
- })
-
- it('user1 have their payment info on the profile card', () => {
- cy.getByDT('openProfileCard').click()
-
- cy.getByDT('profilePaymentMethods').within(() => {
- cy.get('ul').children().should('have.length', 3)
- cy.getByDT('profilePaymentMethod').eq(0).within(() => {
- cy.get('span').eq(0).should('contain', 'bitcoin')
- cy.get('span').eq(1).should('contain', 'h4sh-t0-b3-s4ved')
- })
- cy.getByDT('profilePaymentMethod').eq(1).within(() => {
- cy.get('span').eq(0).should('contain', 'other')
- cy.get('span').eq(1).should('contain', 'IBAN: 12345')
- })
- cy.getByDT('profilePaymentMethod').eq(2).within(() => {
- cy.get('span').eq(0).should('contain', 'other')
- cy.get('span').eq(1).should('contain', 'MBWAY: 91 2345678')
- })
- })
- cy.getByDT('closeProfileCard').click()
- })
-
- const firstContribution = 'Portuguese classes'
-
- it('user1 adds non monetary contribution', () => {
- addNonMonetaryContribution(firstContribution)
-
- cy.getByDT('givingList', 'ul')
- .get('li.is-editable')
- .should('have.length', 1)
- .should('contain', firstContribution)
- })
-
- it('user1 removes non monetary contribution', () => {
- cy.getByDT('buttonEditNonMonetaryContribution')
-
- cy.getByDT('givingList').find('li').should('have.length', 2) // contribution + cta to add
- cy.getByDT('buttonEditNonMonetaryContribution').click()
- cy.getByDT('buttonRemoveNonMonetaryContribution').click()
- cy.getByDT('givingList').find('li').should('have.length', 1) // cta to add
- cy.getByDT('givingParagraph').should('exist')
- })
-
- it('user1 re-adds the same non monetary contribution', () => {
- addNonMonetaryContribution(firstContribution)
- cy.getByDT('givingList', 'ul')
- .get('li.is-editable')
- .should('have.length', 1)
- .should('contain', firstContribution)
- })
-
- it('user1 edits the non monetary contribution', () => {
- cy.getByDT('buttonEditNonMonetaryContribution').click()
- cy.getByDT('inputNonMonetaryContribution').clear().type('French classes{enter}')
- assertNonMonetaryEditableValue('French classes')
- // Double check // TODO - Why do we need this?
- assertNonMonetaryEditableValue('French classes')
-
- cy.getByDT('givingList', 'ul')
- .get('li.is-editable')
- .should('have.length', 1)
- .should('contain', 'French classes')
- })
-
- it('user1 edits it again but cancel it', () => {
- cy.getByDT('buttonEditNonMonetaryContribution').click()
- cy.getByDT('buttonCancelNonMonetaryContribution').click()
- cy.getByDT('givingList', 'ul')
- .get('li.is-editable')
- .should('have.length', 1)
- .should('contain', 'French classes')
- })
-
- it('user1 adds 3 more non monetary contributions', () => {
- addNonMonetaryContribution('German classes')
- addNonMonetaryContribution('Russian classes')
- addNonMonetaryContribution('Korean classes')
-
- cy.getByDT('givingList', 'ul')
- .get('li.is-editable')
- .should('have.length', 4)
-
- assertContributionsWidget({
- nonMonetaryStatus: 'You are contributing.'
- })
- })
-
- it('user1 have their payment info on the member list profile card', () => {
- cy.getByDT('dashboard', 'a').click()
- cy.getByDT('openMemberProfileCard').eq(0).click()
-
- cy.log('The first member card should not contain payment info')
- cy.getByDT('profilePaymentMethods').should('not.exist')
- cy.getByDT('closeProfileCard').click()
-
- cy.log('The last member card should contain payments info')
- cy.getByDT('openMemberProfileCard').eq(3).click()
- cy.getByDT('profilePaymentMethods').within(() => {
- cy.get('ul').children().should('have.length', 3)
- cy.getByDT('profilePaymentMethod').eq(0).within(() => {
- cy.get('span').eq(0).should('contain', 'bitcoin')
- cy.get('span').eq(1).should('contain', 'h4sh-t0-b3-s4ved')
- })
- cy.getByDT('profilePaymentMethod').eq(1).within(() => {
- cy.get('span').eq(0).should('contain', 'other')
- cy.get('span').eq(1).should('contain', 'IBAN: 12345')
- })
- cy.getByDT('profilePaymentMethod').eq(2).within(() => {
- cy.get('span').eq(0).should('contain', 'other')
- cy.get('span').eq(1).should('contain', 'MBWAY: 91 2345678')
- })
- })
-
- cy.getByDT('closeProfileCard').click(('topLeft'))
- })
-
- it('user2 pledges $100 and sees their contributions.', () => {
- cy.giSwitchUser(`user2-${userId}`)
-
- const graphicLegend = [
- 'Total Pledged$100',
- 'Needed Pledges$0'
- ]
- updateIncome(100, false, graphicLegend, '$100 to Greg')
-
- cy.get(elReceivingFirst)
- .should('contain', 'French classes from Greg')
-
- cy.get('.receiving .c-contribution-list')
- .should('have.length', 4)
- })
-
- it('user2 adds 2 non monetary contribution', () => {
- addNonMonetaryContribution('Korean classes')
- addNonMonetaryContribution('French classes')
-
- cy.get('.giving .c-contribution-list')
- .should('have.length', 3)
-
- assertContributionsWidget({
- paymentsSummary: ' ', // TODO - just confirm it exists for now.
- monetaryTitle: 'You are pledging $100',
- monetaryStatus: '$100 will be used.',
- nonMonetaryStatus: 'You and 1 other members are contributing.'
- })
- })
-
- it('user3 pledges $100 and sees who they are pledging to - $50 to user1 (Greg)', () => {
- cy.giSwitchUser(`user3-${userId}`)
- const graphicLegend = [
- 'Total Pledged$200',
- 'Needed Pledges$0'
- ]
- updateIncome(100, false, graphicLegend, '$50 to Greg')
- })
-
- it('user4 and user2 increase their pledges to $500 each. user1 sees the receiving contributions from 3 members.', () => {
- cy.giSwitchUser(`user4-${userId}`)
- const graphicLegend4 = [
- 'Total Pledged$700',
- 'Needed Pledges$0',
- 'Surplus$600'
- ]
- updateIncome(500, false, graphicLegend4, '$71.43 to Greg')
- addNonMonetaryContribution('Korean classes')
-
- cy.giSwitchUser(`user2-${userId}`)
- const graphicLegend2 = [
- 'Total Pledged$1100',
- 'Needed Pledges$0',
- 'Surplus$1000'
- ]
- updateIncome(500, false, graphicLegend2, '$45.45 to Greg')
-
- cy.giSwitchUser(`user1-${userId}`)
-
- cy.getByDT('contributionsLink').click()
- cy.get(elReceivingFirst).should('contain', '$100 from 3 members')
-
- assertContributionsWidget({
- paymentsSummary: ' ', // TODO - just confirm it exists for now.
- monetaryTitle: 'You need $100',
- monetaryStatus: 'You will receive $100.',
- nonMonetaryStatus: 'You and 2 other members are contributing.'
- })
- })
-
- it('user4 and user2 reduced income to $10 and now receive money.', () => {
- cy.giSwitchUser(`user4-${userId}`)
- const graphicLegend4 = [
- 'Total Pledged$600',
- 'Needed Pledges$0',
- 'Surplus$310',
- "You'll receive$190"
- ]
- updateIncome(10, true, graphicLegend4, '$190 from Margarida and Pierre')
-
- cy.giSwitchUser(`user2-${userId}`)
- const graphicLegend2 = [
- 'Total Pledged$100',
- 'Needed Pledges$380',
- "You'll receive$39.58"
- ]
- updateIncome(10, true, graphicLegend2, '$39.58 from Pierre')
-
- assertContributionsWidget({
- paymentsSummary: ' ', // TODO - just confirm it exists for now.
- monetaryTitle: 'You need $190',
- monetaryStatus: 'You will receive $39.58.',
- nonMonetaryStatus: 'You and 2 other members are contributing.'
- })
- })
-
- it('user3 pledges to all 3 members', () => {
- cy.giSwitchUser(`user3-${userId}`)
- cy.getByDT('contributionsLink').click()
-
- cy.get(elGivingFirst)
- .should('contain', 'A total of $100 to 3 members')
- })
-
- it('user1 receives part of what they need', () => {
- cy.giSwitchUser(`user1-${userId}`)
- cy.getByDT('contributionsLink').click()
-
- cy.get(elReceivingFirst)
- .should('contain', '$20.83 from Pierre')
-
- assertContributionsWidget({
- paymentsSummary: ' ', // TODO - just confirm it exists for now.
- monetaryTitle: 'You need $100',
- monetaryStatus: 'You will receive $20.83.',
- nonMonetaryStatus: 'You and 2 other members are contributing.'
- })
- cy.giLogout()
- })
-})
-
-/*
-Summary of the group status so far:
-user1
- - needs $100
- - $20.83 from pierre
-user2
- - needs $190
- - $39.58 from pierre
-user3
- - pledges $100 to user1, user2 and user4
-user4
- - needs $190
- - $39.58 from pierre
-*/