Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ The hash is a list of `key=value` pairs. Keys may repeat.
| `mark` | Marker at a board point: side, then `x,y` in board units, e.g. `mark=F:167.75,144.23`. Drawn on top of everything, on that side only; it does not change the selection. May repeat. Without `side`, the link shows the sides the markers are on. Esc removes them. |
| `zoom` | `board`, `selected` or `highlight`, same as W, E and Z. Used when there is no view. `selected` (and E) fits the highlighted nets as drawn, on layers that are on or that show them with Net, and also fits the markers. |
| `copper` | Shown copper layer, as named in the CAD tool, e.g. `copper=F`. Layers not listed are off; `copper=` alone hides them all. Add `:` and letters to show only some kinds: `t` tracks, `z` zones, `v` vias, `s` silk, `f` fab. `copper=In1.Cu:tv` shows tracks and vias on In1.Cu. |
| `netcopper` | Copper layer that is off but still shows the highlighted nets on it, e.g. `netcopper=In1.Cu`. Takes the same `:LETTERS` as `copper`, e.g. `netcopper=In1.Cu:t` for tracks only. May repeat. Read only together with `copper`; layers not listed show nothing while off. |
| `netcopper` | Copper layer that is off but still shows the highlighted nets on it, e.g. `netcopper=In1.Cu`. May repeat. Read only together with `copper`; layers not listed show nothing while off. Letters after the name, from older links, are ignored. |
| `netlayers` | `1` turns on every layer the linked nets have copper on: tracks, zones or pads. |

Example: `board.html#net=/ADC1/CS&net=GND&netlayers=1&zoom=selected`.
Expand Down
1 change: 0 additions & 1 deletion tests/browser/fit.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ test('E leaves out the side in view when that layer is off', async ({ page }) =>
test('E counts a layer that is off with Net ticked', async ({ page }) => {
const inner = trackBox(INNER_NET, 'In2.Cu');
expect(contains(await fitBoxWith(page, [['side', 'F'], ['copper', 'F'], ['netcopper', 'In2.Cu'], ['net', INNER_NET]]), inner)).toBe(true);
expect(contains(await fitBoxWith(page, [['side', 'F'], ['copper', 'F'], ['netcopper', 'In2.Cu:zv'], ['net', INNER_NET]]), inner)).toBe(false);

// The far side.
const back = trackBox(BACK_NET, 'B');
Expand Down
104 changes: 79 additions & 25 deletions tests/browser/interaction.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -676,24 +676,85 @@ test('old saved layer settings are carried over', async ({ page }) => {
const s = (await state(page)).layers;
expect(s[layer].all).toBe(true);
expect(s.F.all).toBe(true);
expect(s.B.all).toBe(false);
for (const l of ['F', 'B']) {
expect(s[l].silk, l).toBe(false);
expect(s[l].fab, l).toBe(true);
}
// B was off, so it has nothing on.
expect(s.B).toMatchObject({ all: false, silk: false, fab: false });
expect(s.F.silk).toBe(false);
expect(s.F.fab).toBe(true);
});

// Settings saved while All was a switch over the other boxes.
test('saved layer rows with an All switch are carried over', async ({ page }) => {
await load(page);
const [on, off] = innerLayers;
await page.evaluate(([on, off]) => writeStorage('settings', JSON.stringify({ layers: {
F: { all: true, net: false, tracks: true, zones: false, vias: true, silk: true, fab: false },
B: { all: false, net: true, tracks: true, zones: true, vias: true, silk: true, fab: true },
[on]: { all: true, net: false, tracks: false, zones: false, vias: false },
[off]: { all: false, net: false, tracks: true, zones: true, vias: true },
} })), [on, off]);
await page.reload();
await page.waitForFunction(() => window.__pcbaTest && window.__pcbaTest.ready());
const s = (await state(page)).layers;
expect(s.F).toEqual({ all: true, net: false, tracks: true, zones: false, vias: true, silk: true, fab: false });
expect(s.B).toEqual({ all: false, net: true, tracks: false, zones: false, vias: false, silk: false, fab: false });
// On with nothing ticked stays on, with everything.
expect(s[on]).toEqual({ all: true, net: false, tracks: true, zones: true, vias: true });
expect(s[off]).toEqual({ all: false, net: false, tracks: false, zones: false, vias: false });
});

// A row that is off keeps the state of its other boxes.
test('turning a row off and on keeps its other boxes', async ({ page }) => {
// All on a row is ticked when every kind is on, clear when none is, and mixed
// in between. A click on a mixed box turns every kind on.
test('a row All box is clear, mixed or ticked', async ({ page }) => {
await load(page);
await openLayers(page);
const all = layerBox(page, 'F', 'all');
await layerBox(page, 'F', 'vias').uncheck();
await layerBox(page, 'F', 'all').uncheck();
expect(await all.evaluate((cb) => cb.indeterminate)).toBe(true);
await all.click();
expect(await all.evaluate((cb) => cb.indeterminate)).toBe(false);
expect((await state(page)).layers.F).toMatchObject({ tracks: true, zones: true, vias: true, silk: true, fab: true });
await all.uncheck();
await expect(page.locator('#layer-table tr[data-layer="F"]')).toHaveClass(/layer-off/);
await layerBox(page, 'F', 'all').check();
const s = (await state(page)).layers.F;
expect(s.vias).toBe(false);
expect(s.tracks).toBe(true);
expect((await state(page)).layers.F).toMatchObject({ all: false, tracks: false, zones: false, vias: false, silk: false, fab: false });
// Ticking one kind turns the layer back on.
await layerBox(page, 'F', 'tracks').check();
expect((await state(page)).layers.F.all).toBe(true);
expect(await all.evaluate((cb) => cb.indeterminate)).toBe(true);
});

// The All layers row: one box per column for every layer that has it.
test('the All layers row sets a column on every layer', async ({ page }) => {
await load(page);
await openLayers(page);
const layers = ['F', ...innerLayers, 'B'];
const box = (kind) => page.locator(`#layer-table tr.layer-every input[data-kind="${kind}"]`);
const mixed = (kind) => box(kind).evaluate((cb) => cb.indeterminate);

// Only F is on at first.
expect(await mixed('tracks')).toBe(true);
await box('tracks').click();
let s = (await state(page)).layers;
for (const l of layers) expect(s[l].tracks, l).toBe(true);
for (const l of layers) expect(s[l].all, l).toBe(true);
await expect(box('tracks')).toBeChecked();
await box('tracks').click();
s = (await state(page)).layers;
for (const l of layers) expect(s[l].tracks, l).toBe(false);

// Silk is only on F and B. B starts with it off.
expect(await mixed('silk')).toBe(true);
await box('silk').check();
s = (await state(page)).layers;
for (const l of ['F', 'B']) expect(s[l].silk, l).toBe(true);
for (const l of innerLayers) expect(s[l].silk, l).toBeUndefined();

await box('all').check();
s = (await state(page)).layers;
for (const l of layers) expect(s[l].vias, l).toBe(true);
await expect(box('tracks')).toBeChecked();
await box('all').uncheck();
s = (await state(page)).layers;
for (const l of layers) expect(s[l].all, l).toBe(false);
});

test('layer filter limits the net list to nets on that layer', async ({ page }) => {
Expand Down Expand Up @@ -752,12 +813,6 @@ test('Net shows the selected net on an inner layer that is off', async ({ page }
expect((await state(page)).layers[layer]).toMatchObject({ all: false, net: true });
expect(await countColorIn(page, 'IL_comp_hl', PALETTE[0])).toBeGreaterThan(0);
await expect(page.locator('#IL_comp_bg')).toBeHidden();

// The layer's own boxes still pick what is drawn.
await layerBox(page, layer, 'tracks').uncheck();
await layerBox(page, layer, 'zones').uncheck();
await layerBox(page, layer, 'vias').uncheck();
expect(await countColorIn(page, 'IL_comp_hl', PALETTE[0])).toBe(0);
});

// Net on the far side draws the net's far side tracks on the viewed side.
Expand Down Expand Up @@ -946,15 +1001,14 @@ test('T ticks Net on a layer where the selected net has only pads', async ({ pag
expect((await state(page)).layers.B.net).toBe(true);
});

// On a row that is off with Net ticked, tracks, zones and vias pick what is
// drawn, so they are not dimmed. Silk stays dimmed.
test('Net undims tracks, zones and vias on a row that is off', async ({ page }) => {
// Net matters only on a row that is off, so it is dimmed on a row that is on.
// The other boxes are never dimmed.
test('Net is dimmed only on a row that is on', async ({ page }) => {
await load(page);
await openLayers(page);
await layerBox(page, 'B', 'net').check();
const opacity = (kind) => layerBox(page, 'B', kind).evaluate((el) => getComputedStyle(el).opacity);
for (const k of ['tracks', 'zones', 'vias', 'net']) expect(await opacity(k), k).toBe('1');
expect(await opacity('silk')).not.toBe('1');
const opacity = (l, kind) => layerBox(page, l, kind).evaluate((el) => getComputedStyle(el).opacity);
expect(await opacity('F', 'net')).not.toBe('1');
for (const k of ['all', 'tracks', 'zones', 'vias', 'silk', 'net']) expect(await opacity('B', k), k).toBe('1');
});

test('M toggles dark mode', async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion tests/browser/layers.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test('inner layer list is in number order', async ({ page }) => {
const rows = await page.locator('#layer-table tbody tr')
.evaluateAll((trs) => trs.map((tr) => tr.dataset.layer));
expect(rows).toEqual(['F', ...INNER, 'B']);
const names = await page.locator('#layer-table td.layer-name').allTextContents();
const names = await page.locator('#layer-table tbody td.layer-name').allTextContents();
expect(names.map((l) => l.trim())).toEqual(['F', ...INNER, 'B']);
});

Expand Down
24 changes: 16 additions & 8 deletions tests/browser/links.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -237,12 +237,13 @@ test('a link carries layers that show only the highlighted nets', async ({ page
expect(pairs).not.toContainEqual(['netcopper', 'B']);
});

test('a netcopper link carries which kinds are drawn', async ({ page }) => {
// Older links wrote which kinds to draw. A layer that is off has none now.
test('letters after a netcopper layer are ignored', async ({ page }) => {
await load(page, hashOf([['copper', 'F'], ['netcopper', innerLayer + ':t']]));
const s = await state(page);
expect(s.layers[innerLayer]).toMatchObject({ all: false, net: true, tracks: true, zones: false, vias: false });
expect(s.layers[innerLayer]).toMatchObject({ all: false, net: true, tracks: false, zones: false, vias: false });
const pairs = await page.evaluate(() => shareLinkPairs({ layers: true }));
expect(pairs).toContainEqual(['netcopper', innerLayer + ':t']);
expect(pairs).toContainEqual(['netcopper', innerLayer]);
});

test('a link sets side and layers for this visit only', async ({ page }) => {
Expand All @@ -261,7 +262,9 @@ test('a link sets side and layers for this visit only', async ({ page }) => {
expect(s.layers.B).toEqual({ all: true, net: false, tracks: true, zones: true, vias: true, silk: true, fab: false });
expect(s.layers[innerLayer]).toEqual({ all: true, net: false, tracks: true, zones: false, vias: true });
await page.locator('#inner-layers-menu .menu-btn').hover();
await expect(layerBox(page, innerLayer, 'all')).toBeChecked();
// Some kinds on: All is mixed.
expect(await layerBox(page, innerLayer, 'all').evaluate((cb) => cb.indeterminate)).toBe(true);
await expect(layerBox(page, 'B', 'all')).not.toBeChecked();
await expect(layerBox(page, innerLayer, 'zones')).not.toBeChecked();
await expect(layerBox(page, 'F', 'all')).not.toBeChecked();

Expand All @@ -270,11 +273,11 @@ test('a link sets side and layers for this visit only', async ({ page }) => {
await page.keyboard.press('o');
const stored = await storedSettings(page);
expect(stored.layers.B.fab).toBe(true);
expect(stored.layers.B.all).toBe(false);
expect(stored.layers.F.all).toBe(true);
expect(stored.layers.B.tracks).not.toBe(true);
expect(stored.layers.F.tracks).toBe(true);
expect(stored.canvaslayout).toBe(before.canvasLayout);
expect(stored.layers[innerLayer].all).toBe(false);
expect(stored.layers[innerLayer].zones).toBe(true);
expect(stored.layers[innerLayer].tracks).not.toBe(true);
expect(stored.layers[innerLayer].vias).not.toBe(true);

// A setting the link set is saved once the user changes it.
await page.keyboard.press('g');
Expand All @@ -300,6 +303,11 @@ test('old layers, xray and overlay keys still work', async ({ page }) => {
expect(s.layers.F.all).toBe(true);
expect(s.layers.B.all).toBe(false);
expect(s.layers[innerLayer].all).toBe(false);

await load(page, hashOf([['side', 'F'], ['overlay', 'silk']]));
s = await state(page);
expect(s.layers.F.silk).toBe(true);
expect(s.layers.B.all).toBe(false);
});

test('netlayers=1 turns on the layers of the linked nets', async ({ page }) => {
Expand Down
2 changes: 1 addition & 1 deletion tests/browser/smoke.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async function load(page) {
test('test API is present at the expected version', async ({ page }) => {
await load(page);
const version = await page.evaluate(() => window.__pcbaTest.version);
expect(version).toBe(2);
expect(version).toBe(3);
});

test('board loads with no page errors', async ({ page }) => {
Expand Down
Loading
Loading