Compare commits
No commits in common. "b0630a25e117c176c12654465c7cca6d9ea51e65" and "4a0ed993298ebe4bbd9a7b1eff087dc8207e4fc9" have entirely different histories.
b0630a25e1
...
4a0ed99329
9 changed files with 13 additions and 133 deletions
3
Taskfile
3
Taskfile
|
|
@ -12,8 +12,7 @@ refresh_sqlx_db() {
|
||||||
rm -f some_user.db
|
rm -f some_user.db
|
||||||
for migration in migrations/each_user/*.sql; do
|
for migration in migrations/each_user/*.sql; do
|
||||||
echo "Applying $migration..."
|
echo "Applying $migration..."
|
||||||
echo "BEGIN TRANSACTION;$(cat "$migration");COMMIT TRANSACTION;"\
|
sqlite3 some_user.db < "$migration"
|
||||||
| sqlite3 some_user.db
|
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import { expect, type Locator } from '@playwright/test';
|
|
||||||
expect.extend({
|
|
||||||
async toBeAbove(self: Locator, other: Locator) {
|
|
||||||
const name = 'toBeAbove';
|
|
||||||
let pass: boolean;
|
|
||||||
let matcherResult: any;
|
|
||||||
let selfY: number | null = null;
|
|
||||||
let otherY: number | null = null;
|
|
||||||
try {
|
|
||||||
selfY = (await self.boundingBox())?.y ?? null;
|
|
||||||
otherY = (await self.boundingBox())?.y ?? null;
|
|
||||||
pass = selfY !== null && otherY !== null && (selfY < otherY);
|
|
||||||
} catch (e: any) {
|
|
||||||
matcherResult = e.matcherResult;
|
|
||||||
pass = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isNot) {
|
|
||||||
pass =!pass;
|
|
||||||
}
|
|
||||||
|
|
||||||
const message = () => this.utils.matcherHint(name, undefined, undefined, { isNot: this.isNot }) +
|
|
||||||
'\n\n' +
|
|
||||||
`Locator: ${self}\n` +
|
|
||||||
`Expected: above ${other} (y=${this.utils.printExpected(otherY)})\n` +
|
|
||||||
(matcherResult ? `Received: y=${this.utils.printReceived(selfY)}` : '');
|
|
||||||
|
|
||||||
return {
|
|
||||||
message,
|
|
||||||
pass,
|
|
||||||
name,
|
|
||||||
expected: (this.isNot ? '>=' : '<') + otherY,
|
|
||||||
actual: selfY,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
import { test, expect } from '@playwright/test';
|
|
||||||
import { login, verifyCreateUser, todate } from './util';
|
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await login(page);
|
|
||||||
await verifyCreateUser(page, { names: ['Test Testerson'] });
|
|
||||||
await expect(page.locator('#alpine-loaded')).not.toHaveAttribute('x-cloak');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('manual-freshen date is editable', async ({ page }) => {
|
|
||||||
await page.getByRole('link', { name: /edit/i }).click();
|
|
||||||
await expect(page.getByRole('textbox', { name: /freshened/i })).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('last-contact date on display resolves journal mentions and manual-freshen', async ({ page }) => {
|
|
||||||
const today = new Date().toISOString().split("T")[0];
|
|
||||||
const todayRe = new RegExp(today.substring(0, today.length - 1) + ".");
|
|
||||||
const entryDate = page.getByPlaceholder(todayRe);
|
|
||||||
const entryBox = page.getByPlaceholder(/new entry/i);
|
|
||||||
await entryDate.fill("2025-05-05");
|
|
||||||
await entryBox.fill("[[Test Testerson]]");
|
|
||||||
await page.getByRole('button', { name: /add entry/i }).click();
|
|
||||||
await page.reload();
|
|
||||||
await expect(page.locator('#fields')).toContainText("freshened2025-05-05");
|
|
||||||
});
|
|
||||||
|
|
||||||
test.skip("groups wrap nicely", async ({ page }) => {
|
|
||||||
await page.getByRole('link', { name: /edit/i }).click();
|
|
||||||
await expect(page.locator('#alpine-loaded')).not.toHaveAttribute('x-cloak');
|
|
||||||
|
|
||||||
const groupBox = page.getByPlaceholder(/group name/i);
|
|
||||||
await groupBox.fill('this is a long group name');
|
|
||||||
await page.getByRole('button', { name: /save/i }).click();
|
|
||||||
await expect(page.locator('#alpine-loaded')).not.toHaveAttribute('x-cloak');
|
|
||||||
|
|
||||||
// TODO: this drives to the right location but i can't figure out how to assert
|
|
||||||
// that the text is all on one line. Manual inspection looks good at time of writing.
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allow marking as hidden', async ({ page }) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allow exempting from stale', async ({ page }) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
test('something is fucky with lives_with insertion triggering mention generation', async ({ page }) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
test('bullet points in free text display well', async ({ page }) => {
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
/*
|
|
||||||
home: contact list scrolls in screen, not off screen
|
|
||||||
home: clicking off contact list closes it
|
|
||||||
home: contact list is sorted ignoring case
|
|
||||||
home: contact list should scroll to current contact in center of view
|
|
||||||
journal: saving journal entry should stay in edit
|
|
||||||
journal: sometimes editing entries fucks up mentions (probably another $1/$2 error)
|
|
||||||
journal: bullet points don't display
|
|
||||||
*/
|
|
||||||
|
|
@ -1,26 +1,28 @@
|
||||||
import { test, expect } from '@playwright/test';
|
import { test, expect } from '@playwright/test';
|
||||||
import { login, verifyCreateUser, todate } from './util';
|
import { login, verifyCreateUser, todate } from './util';
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await login(page);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('can log out', async ({ page }) => {
|
test('can log out', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
|
|
||||||
await page.getByText("Logout").click();
|
await page.getByText("Logout").click();
|
||||||
await expect(page.getByLabel("Username")).toBeVisible();
|
await expect(page.getByLabel("Username")).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('has no contacts', async ({ page }) => {
|
test('has no contacts', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
|
|
||||||
await expect(page.getByRole("navigation").getByRole("link")).toHaveCount(0);
|
await expect(page.getByRole("navigation").getByRole("link")).toHaveCount(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can add contacts', async ({ page }) => {
|
test('can add contacts', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
await verifyCreateUser(page, { names: ['John Contact'] });
|
await verifyCreateUser(page, { names: ['John Contact'] });
|
||||||
await verifyCreateUser(page, { names: ['Jack Contact'] });
|
await verifyCreateUser(page, { names: ['Jack Contact'] });
|
||||||
await expect(page.getByRole("navigation").getByRole("link")).toHaveCount(2);
|
await expect(page.getByRole("navigation").getByRole("link")).toHaveCount(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shows "never" for unfreshened contacts', async ({ page }) => {
|
test('shows "never" for unfreshened contacts', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
await verifyCreateUser(page, { names: ['John Contact'] });
|
await verifyCreateUser(page, { names: ['John Contact'] });
|
||||||
await page.getByRole('link', { name: 'Mascarpone' }).click();
|
await page.getByRole('link', { name: 'Mascarpone' }).click();
|
||||||
|
|
||||||
|
|
@ -28,6 +30,7 @@ test('shows "never" for unfreshened contacts', async ({ page }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('shows the date for fresh contacts', async ({ page }) => {
|
test('shows the date for fresh contacts', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
await verifyCreateUser(page, { names: ['John Contact'] });
|
await verifyCreateUser(page, { names: ['John Contact'] });
|
||||||
await page.getByRole('link', { name: /edit/i }).click();
|
await page.getByRole('link', { name: /edit/i }).click();
|
||||||
await page.getByRole('button', { name: /fresh/i }).click();
|
await page.getByRole('button', { name: /fresh/i }).click();
|
||||||
|
|
@ -37,6 +40,7 @@ test('shows the date for fresh contacts', async ({ page }) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('sidebar is sorted alphabetically', async ({ page }) => {
|
test('sidebar is sorted alphabetically', async ({ page }) => {
|
||||||
|
await login(page);
|
||||||
await verifyCreateUser(page, { names: ['Zulu'] });
|
await verifyCreateUser(page, { names: ['Zulu'] });
|
||||||
await verifyCreateUser(page, { names: ['Alfa'] });
|
await verifyCreateUser(page, { names: ['Alfa'] });
|
||||||
await verifyCreateUser(page, { names: ['Golf'] });
|
await verifyCreateUser(page, { names: ['Golf'] });
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import { defineConfig, devices } from '@playwright/test';
|
import { defineConfig, devices } from '@playwright/test';
|
||||||
import 'custom-expects';
|
|
||||||
|
|
||||||
// purposefully not using ??: we want to replace empty empty string with default
|
// purposefully not using ??: we want to replace empty empty string with default
|
||||||
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
const BASE_URL = process.env.BASE_URL || 'http://localhost:3000';
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
-- foreign_keys can only up/down outside of transactions
|
|
||||||
-- so we first pre-commit the one started by sqlx...
|
|
||||||
COMMIT TRANSACTION;
|
|
||||||
|
|
||||||
-- turn off foreign keys...
|
|
||||||
PRAGMA foreign_keys=OFF;
|
PRAGMA foreign_keys=OFF;
|
||||||
|
|
||||||
-- start our own transaction...
|
|
||||||
BEGIN TRANSACTION;
|
|
||||||
create table if not exists new_contacts (
|
create table if not exists new_contacts (
|
||||||
id integer primary key autoincrement,
|
id integer primary key autoincrement,
|
||||||
birthday text,
|
birthday text,
|
||||||
|
|
@ -23,12 +16,4 @@ insert into new_contacts (
|
||||||
drop table contacts;
|
drop table contacts;
|
||||||
alter table new_contacts rename to contacts;
|
alter table new_contacts rename to contacts;
|
||||||
PRAGMA foreign_key_check;
|
PRAGMA foreign_key_check;
|
||||||
|
|
||||||
-- commit our own transaction...
|
|
||||||
COMMIT TRANSACTION;
|
|
||||||
|
|
||||||
-- put our own pragmas back...
|
|
||||||
PRAGMA foreign_keys=ON;
|
PRAGMA foreign_keys=ON;
|
||||||
|
|
||||||
-- and start a dummy transaction so sqlx's COMMIT doesn't explode
|
|
||||||
BEGIN TRANSACTION;
|
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ mod get {
|
||||||
let entries: Vec<JournalEntry> = sqlx::query_as(
|
let entries: Vec<JournalEntry> = sqlx::query_as(
|
||||||
"select distinct j.id, j.value, j.date from journal_entries j
|
"select distinct j.id, j.value, j.date from journal_entries j
|
||||||
join mentions m on j.id = m.entity_id
|
join mentions m on j.id = m.entity_id
|
||||||
where m.entity_type = $1 and (m.url = '/contact/'||$2 or m.url in (
|
where m.entity_type = $1 and (m.url = '/contact/'||$1 or m.url in (
|
||||||
select '/group/'||slug from groups
|
select '/group/'||slug from groups
|
||||||
where contact_id = $2
|
where contact_id = $2
|
||||||
))
|
))
|
||||||
|
|
@ -87,11 +87,6 @@ mod get {
|
||||||
.fetch_all(pool)
|
.fetch_all(pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let freshened = std::cmp::max(
|
|
||||||
contact.manually_freshened_at.map(|when| when.date_naive()),
|
|
||||||
entries.get(0).map(|entry| entry.date),
|
|
||||||
);
|
|
||||||
|
|
||||||
let phone_numbers: Vec<PhoneNumber> = sqlx::query_as!(
|
let phone_numbers: Vec<PhoneNumber> = sqlx::query_as!(
|
||||||
PhoneNumber,
|
PhoneNumber,
|
||||||
"select * from phone_numbers where contact_id = $1",
|
"select * from phone_numbers where contact_id = $1",
|
||||||
|
|
@ -146,8 +141,8 @@ mod get {
|
||||||
}
|
}
|
||||||
label { "freshened" }
|
label { "freshened" }
|
||||||
div {
|
div {
|
||||||
@if let Some(freshened) = freshened {
|
@if let Some(when) = &contact.manually_freshened_at {
|
||||||
(freshened.to_string())
|
(when.date_naive().to_string())
|
||||||
} @else {
|
} @else {
|
||||||
"(never)"
|
"(never)"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,6 @@ impl Layout {
|
||||||
(content)
|
(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template #alpine-loaded x-cloak {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ main {
|
||||||
#groups {
|
#groups {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: fit-content;
|
width: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
#text_body {
|
#text_body {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue