pixalynx/edge-connector

Configures USB-A, Micro-USB, and USB-C PCB-edge connectors with exposed copper contacts and automatically shaped, positioned board tongues.

Version
1.0.0
License
GPL-3.0-only
Stars
0

UsbAEdgeConnector.test.mjs

import { expect, test } from "bun:test"
import { jsx } from "react/jsx-runtime"
import { Circuit } from "tscircuit"
import { UsbAEdgeConnector } from "./index.tsx"
import Example from "./index.circuit.tsx"

async function render(element) {
  const circuit = new Circuit()
  circuit.add(element)
  await circuit.renderUntilSettled()
  return circuit.getCircuitJson()
}

function padsBySignal(json) {
  return Object.fromEntries(json.filter(e => e.type === "pcb_smtpad").map(pad => {
    const pcbPort = json.find(e => e.type === "pcb_port" && e.pcb_port_id === pad.pcb_port_id)
    const sourcePort = json.find(e => e.type === "source_port" && e.source_port_id === pcbPort.source_port_id)
    return [sourcePort.name, pad]
  }))
}

test("USB pin mapping, staggered contacts, exposed mask, and no paste", async () => {
  const json = await render(jsx("board", {
    width: 30, height: 30,
    children: jsx(UsbAEdgeConnector, { name: "J1", pcbX: 0, pcbY: 0 }),
  }))
  const pads = padsBySignal(json)
  expect(Object.keys(pads)).toEqual(["VBUS", "DM", "DP", "GND"])
  expect(Object.values(pads).map(p => p.x)).toEqual([-3.5, -1, 1, 3.5])
  expect(Object.values(pads).map(p => p.y - p.height / 2)).toEqual([-3.75, -3.75, -3.75, -3.75])
  expect(pads.VBUS.y + pads.VBUS.height / 2 - pads.DM.y - pads.DM.height / 2).toBe(1)
  expect(pads.GND.height).toBe(pads.VBUS.height)
  for (const pad of Object.values(pads)) {
    expect(pad.layer).toBe("top")
    expect(pad.is_covered_with_solder_mask).toBe(false)
    expect(pad.soldermask_margin).toBe(0.05)
  }
  expect(json.filter(e => e.type === "pcb_solder_paste")).toHaveLength(0)
  const ports = json.filter(e => e.type === "source_port")
  expect(ports.map(p => [p.pin_number, p.name])).toEqual([[1, "VBUS"], [2, "DM"], [3, "DP"], [4, "GND"]])
})

test("placement and 90-degree rotation preserve signal-to-pad mapping", async () => {
  const json = await render(jsx("board", {
    width: 40, height: 40,
    children: jsx(UsbAEdgeConnector, { name: "J2", pcbX: 5, pcbY: 3, pcbRotation: 90 }),
  }))
  const pads = padsBySignal(json)
  for (const [signal, x, y] of [["VBUS", 5, -0.5], ["DM", 5.5, 2], ["DP", 5.5, 4], ["GND", 5, 6.5]]) {
    expect(pads[signal].x).toBeCloseTo(x)
    expect(pads[signal].y).toBeCloseTo(y)
  }
  expect(json.filter(e => e.type === "pcb_solder_paste")).toHaveLength(0)
})

test("example puts all four contacts inside the 2 mm PCB tongue", async () => {
  const json = await render(jsx(Example, {}))
  const board = json.find(e => e.type === "pcb_board")
  expect(board.thickness).toBe(2)
  const tip = Math.max(...board.outline.map(p => p.y))
  for (const pad of Object.values(padsBySignal(json))) {
    expect(Math.abs(pad.x) + pad.width / 2).toBeLessThan(6)
    expect(tip - pad.y - pad.height / 2).toBeGreaterThanOrEqual(1)
    expect(pad.y - pad.height / 2).toBeGreaterThan(tip - 8.7)
  }
  expect(json.filter(e => /error$/.test(e.type))).toEqual([])
})