forked from trent_larson/crowd-funder-for-time-pwa
fix list of offers (and some other lists), and add tests for offers
This commit is contained in:
@@ -6,7 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
|
||||||
## [0.3.16] - 2024.07.10
|
## [0.3.18] - 2024.07.12
|
||||||
|
### Fixed
|
||||||
|
- List of offers wasn't showing.
|
||||||
|
|
||||||
|
|
||||||
|
## [0.3.17] - 2024.07.11 - cefa384ff1a2d922848c370640c096c529920fab
|
||||||
### Added
|
### Added
|
||||||
- Photos on more screens
|
- Photos on more screens
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
/* Configure global timeout */
|
/* Configure global timeout; default is 30000 milliseconds */
|
||||||
// the image upload will often not succeed at 5 seconds
|
// the image upload will often not succeed at 5 seconds
|
||||||
//timeout: 7000,
|
//timeout: 10000,
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
/* Run your local dev server before starting the tests */
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<h1 class="text-xl font-bold text-center mb-4">Offer Help</h1>
|
<h1 class="text-xl font-bold text-center mb-4">Offer Help</h1>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
|
data-testId="inputDescription"
|
||||||
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
class="block w-full rounded border border-slate-400 mb-2 px-3 py-2"
|
||||||
placeholder="Description, prerequisites, terms, etc."
|
placeholder="Description, prerequisites, terms, etc."
|
||||||
v-model="description"
|
v-model="description"
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
<fa icon="chevron-left" />
|
<fa icon="chevron-left" />
|
||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
|
data-testId="inputOfferAmount"
|
||||||
type="number"
|
type="number"
|
||||||
class="w-full border border-r-0 border-slate-400 px-2 py-2 text-center"
|
class="w-full border border-r-0 border-slate-400 px-2 py-2 text-center"
|
||||||
v-model="amountInput"
|
v-model="amountInput"
|
||||||
|
|||||||
@@ -831,10 +831,16 @@ export default class AccountViewView extends Vue {
|
|||||||
* Beware! I've seen where we never get to this point because "ready" never resolves.
|
* Beware! I've seen where we never get to this point because "ready" never resolves.
|
||||||
*/
|
*/
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
// this can happen when running automated tests in dev mode because notifications don't work
|
||||||
console.error(
|
console.error(
|
||||||
"Telling user to clear cache at page create because:",
|
"Telling user to clear cache at page create because:",
|
||||||
error,
|
error,
|
||||||
);
|
);
|
||||||
|
// this sometimes gives different information on the error
|
||||||
|
console.error(
|
||||||
|
"Telling user to clear cache at page create because (error added): " +
|
||||||
|
error,
|
||||||
|
);
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
@@ -1282,17 +1288,6 @@ export default class AccountViewView extends Vue {
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.handleRateLimitsError(error);
|
this.handleRateLimitsError(error);
|
||||||
|
|
||||||
try {
|
|
||||||
await db.open();
|
|
||||||
await db.settings.update(MASTER_SETTINGS_KEY, {
|
|
||||||
isRegistered: false,
|
|
||||||
});
|
|
||||||
this.isRegistered = false;
|
|
||||||
} catch (err) {
|
|
||||||
console.error("Got an error marking user not registered:", err);
|
|
||||||
// already set an error notification for the user
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.loadingLimits = false;
|
this.loadingLimits = false;
|
||||||
|
|||||||
@@ -51,7 +51,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<fa icon="message" class="fa-fw text-slate-400" />
|
<fa icon="message" class="fa-fw text-slate-400" />
|
||||||
{{ veriClaim.claim?.description }}
|
{{
|
||||||
|
veriClaim.claim?.description ||
|
||||||
|
veriClaim.claim?.itemOffered?.description
|
||||||
|
}}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<fa icon="user" class="fa-fw text-slate-400" />
|
<fa icon="user" class="fa-fw text-slate-400" />
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export default class ContactAmountssView extends Vue {
|
|||||||
// Make the xhr request payload
|
// Make the xhr request payload
|
||||||
const payload = JSON.stringify({ jwtEncoded: vcJwt });
|
const payload = JSON.stringify({ jwtEncoded: vcJwt });
|
||||||
const url = this.apiServer + "/api/v2/claim";
|
const url = this.apiServer + "/api/v2/claim";
|
||||||
const headers = getHeaders(this.activeDid) as AxiosRequestHeaders;
|
const headers = (await getHeaders(this.activeDid)) as AxiosRequestHeaders;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const resp = await this.axios.post(url, payload, { headers });
|
const resp = await this.axios.post(url, payload, { headers });
|
||||||
|
|||||||
@@ -265,6 +265,8 @@ export default class DiscoverView extends Vue {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error("Error with feed load:", e);
|
console.error("Error with feed load:", e);
|
||||||
|
// this sometimes gives different information
|
||||||
|
console.error("Error with feed load (error added): " + e);
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ export default class NewEditProjectView extends Vue {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const headers = getHeaders(this.activeDid) as AxiosRequestHeaders;
|
const headers = (await getHeaders(this.activeDid)) as AxiosRequestHeaders;
|
||||||
const response = await this.axios.delete(
|
const response = await this.axios.delete(
|
||||||
DEFAULT_IMAGE_API_SERVER +
|
DEFAULT_IMAGE_API_SERVER +
|
||||||
"/image/" +
|
"/image/" +
|
||||||
|
|||||||
@@ -162,6 +162,7 @@
|
|||||||
<div v-if="activeDid && isRegistered" class="mt-4">
|
<div v-if="activeDid && isRegistered" class="mt-4">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button
|
<button
|
||||||
|
data-testId="offerButton"
|
||||||
@click="openOfferDialog()"
|
@click="openOfferDialog()"
|
||||||
class="block w-full text-lg font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
|
class="block w-full text-lg font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -396,7 +396,7 @@ export default class ProjectsView extends Vue {
|
|||||||
* @param token Authorization token
|
* @param token Authorization token
|
||||||
**/
|
**/
|
||||||
async offerDataLoader(url: string) {
|
async offerDataLoader(url: string) {
|
||||||
const headers = getHeaders(this.activeDid);
|
const headers = await getHeaders(this.activeDid);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.isLoading = true;
|
this.isLoading = true;
|
||||||
@@ -443,7 +443,7 @@ export default class ProjectsView extends Vue {
|
|||||||
async loadMoreOfferData(payload: boolean) {
|
async loadMoreOfferData(payload: boolean) {
|
||||||
if (this.offers.length > 0 && payload) {
|
if (this.offers.length > 0 && payload) {
|
||||||
const latestOffer = this.offers[this.offers.length - 1];
|
const latestOffer = this.offers[this.offers.length - 1];
|
||||||
await this.loadOffers(this.activeDid, `&beforeId=${latestOffer.jwtId}`);
|
await this.loadOffers(`&beforeId=${latestOffer.jwtId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,8 +452,8 @@ export default class ProjectsView extends Vue {
|
|||||||
* @param issuerDid of the user
|
* @param issuerDid of the user
|
||||||
* @param urlExtra additional url parameters in a string
|
* @param urlExtra additional url parameters in a string
|
||||||
**/
|
**/
|
||||||
async loadOffers(issuerDid?: string, urlExtra: string = "") {
|
async loadOffers(urlExtra: string = "") {
|
||||||
const url = `${this.apiServer}/api/v2/report/offers?offeredByDid=${issuerDid}${urlExtra}`;
|
const url = `${this.apiServer}/api/v2/report/offers?offeredByDid=${this.activeDid}${urlExtra}`;
|
||||||
await this.offerDataLoader(url);
|
await this.offerDataLoader(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,9 +54,11 @@
|
|||||||
service code underneath. To fix this, first make sure you have latest
|
service code underneath. To fix this, first make sure you have latest
|
||||||
version by comparing your version at the bottom of "Help" with the
|
version by comparing your version at the bottom of "Help" with the
|
||||||
version at the bottom of https://timesafari.app/help in a browser. After
|
version at the bottom of https://timesafari.app/help in a browser. After
|
||||||
that, it may eventually work, but you can speed up the process by clearing
|
that, it may eventually work, but you can speed up the process by
|
||||||
your data cache (in the browser on mobile, even if you installed it) and/or
|
clearing your data cache (in the browser on mobile, even if you
|
||||||
reinstalling the app (after backing up all your data, of course).</p>
|
installed it) and/or reinstalling the app (after backing up all your
|
||||||
|
data, of course).
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -43,12 +43,10 @@ test('Create new project, then search for it', async ({ page }) => {
|
|||||||
// Search for newly-created project in /projects
|
// Search for newly-created project in /projects
|
||||||
await page.goto('./projects');
|
await page.goto('./projects');
|
||||||
await page.getByRole('link', { name: 'Projects', exact: true }).click();
|
await page.getByRole('link', { name: 'Projects', exact: true }).click();
|
||||||
await page.waitForTimeout(3000); // Wait for a bit
|
|
||||||
await expect(page.locator('ul#listProjects li.border-b:nth-child(1)')).toContainText(finalRandomString); // Assumes newest project always appears first in the Projects tab list
|
await expect(page.locator('ul#listProjects li.border-b:nth-child(1)')).toContainText(finalRandomString); // Assumes newest project always appears first in the Projects tab list
|
||||||
|
|
||||||
// Search for newly-created project in /discover
|
// Search for newly-created project in /discover
|
||||||
await page.goto('./discover');
|
await page.goto('./discover');
|
||||||
await page.waitForTimeout(3000); // Wait for a bit
|
|
||||||
await page.getByPlaceholder('Search…').fill(finalRandomString);
|
await page.getByPlaceholder('Search…').fill(finalRandomString);
|
||||||
await page.locator('#QuickSearch button').click();
|
await page.locator('#QuickSearch button').click();
|
||||||
await expect(page.locator('ul#listDiscoverResults li.border-b:nth-child(1)')).toContainText(finalRandomString);
|
await expect(page.locator('ul#listDiscoverResults li.border-b:nth-child(1)')).toContainText(finalRandomString);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ test('Record something given', async ({ page }) => {
|
|||||||
await page.goto('./');
|
await page.goto('./');
|
||||||
await page.getByRole('heading', { name: 'Unnamed/Unknown' }).click();
|
await page.getByRole('heading', { name: 'Unnamed/Unknown' }).click();
|
||||||
await page.getByPlaceholder('What was given').fill(finalTitle);
|
await page.getByPlaceholder('What was given').fill(finalTitle);
|
||||||
await page.getByRole('spinbutton', { id: 'inputGivenAmount' }).fill(randomNonZeroNumber.toString());
|
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
||||||
|
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ test('Add contact, record gift, confirm gift', async ({ page }) => {
|
|||||||
// Record something given by new contact
|
// Record something given by new contact
|
||||||
await page.getByRole('heading', { name: contactName }).click();
|
await page.getByRole('heading', { name: contactName }).click();
|
||||||
await page.getByPlaceholder('What was given').fill(finalTitle);
|
await page.getByPlaceholder('What was given').fill(finalTitle);
|
||||||
await page.getByRole('spinbutton').fill(randomNonZeroNumber.toString());
|
await page.getByRole('spinbutton', { id: 'inputGivenAmount' }).fill(randomNonZeroNumber.toString());
|
||||||
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
await expect(page.getByText('That gift was recorded.')).toBeVisible();
|
||||||
|
|
||||||
|
|||||||
33
test-playwright/50-record-offer.spec.ts
Normal file
33
test-playwright/50-record-offer.spec.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
import { importUser } from './testUtils';
|
||||||
|
|
||||||
|
test('Record an offer', async ({ page }) => {
|
||||||
|
// Generate a random string of 6 characters, skipping the "0." at the beginning
|
||||||
|
const randomString = Math.random().toString(36).substring(2, 8);
|
||||||
|
// Standard title prefix
|
||||||
|
const finalTitle = `Offer ${randomString}`;
|
||||||
|
const randomNonZeroNumber = Math.floor(Math.random() * 999) + 1;
|
||||||
|
|
||||||
|
// Create new ID for default user
|
||||||
|
await importUser(page);
|
||||||
|
|
||||||
|
// Select a project
|
||||||
|
await page.goto('./discover');
|
||||||
|
await page.locator('ul#listDiscoverResults li:nth-child(1)').click();
|
||||||
|
|
||||||
|
// Record an offer
|
||||||
|
await page.getByTestId('offerButton').click();
|
||||||
|
await page.getByTestId('inputDescription').fill(finalTitle);
|
||||||
|
await page.getByTestId('inputOfferAmount').fill(randomNonZeroNumber.toString());
|
||||||
|
await page.getByRole('button', { name: 'Sign & Send' }).click();
|
||||||
|
await expect(page.getByText('That offer was recorded.')).toBeVisible();
|
||||||
|
|
||||||
|
// Refresh home view and check gift
|
||||||
|
await page.goto('./projects');
|
||||||
|
await page.locator('li').filter({ hasText: `All ${randomNonZeroNumber} remaining` }).locator('a').first().click();
|
||||||
|
await expect(page.getByRole('heading', { name: 'Verifiable Claim Details' })).toBeVisible();
|
||||||
|
await expect(page.getByText(finalTitle, { exact: true })).toBeVisible();
|
||||||
|
const page1Promise = page.waitForEvent('popup');
|
||||||
|
await page.getByRole('link', { name: 'View on the Public Server' }).click();
|
||||||
|
const page1 = await page1Promise;
|
||||||
|
});
|
||||||
@@ -21,6 +21,8 @@ export async function importUser(page, id) {
|
|||||||
await page.getByText('You have a seed').click();
|
await page.getByText('You have a seed').click();
|
||||||
await page.getByPlaceholder('Seed Phrase').fill(seedPhrase);
|
await page.getByPlaceholder('Seed Phrase').fill(seedPhrase);
|
||||||
await page.getByRole('button', { name: 'Import' }).click();
|
await page.getByRole('button', { name: 'Import' }).click();
|
||||||
|
await expect(page.locator('#sectionUsageLimits')).toContainText('You have done');
|
||||||
|
await expect(page.locator('#sectionUsageLimits')).toContainText('You have uploaded');
|
||||||
|
|
||||||
// Set name
|
// Set name
|
||||||
await page.getByRole('link', { name: 'Set Your Name' }).click();
|
await page.getByRole('link', { name: 'Set Your Name' }).click();
|
||||||
|
|||||||
Reference in New Issue
Block a user