mirror of
https://github.com/acamarata/qibla.git
synced 2026-06-30 19:04:28 +00:00
184 lines
7.1 KiB
JavaScript
184 lines
7.1 KiB
JavaScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import {
|
|
qiblaAngle,
|
|
compassDir,
|
|
compassName,
|
|
qiblaGreatCircle,
|
|
distanceKm,
|
|
KAABA_LAT,
|
|
KAABA_LNG,
|
|
EARTH_RADIUS_KM,
|
|
} from "./dist/index.mjs";
|
|
|
|
describe("KAABA constants", () => {
|
|
it("latitude is approximately 21.42°N", () => {
|
|
assert.ok(Math.abs(KAABA_LAT - 21.42) < 0.1);
|
|
});
|
|
it("longitude is approximately 39.83°E", () => {
|
|
assert.ok(Math.abs(KAABA_LNG - 39.83) < 0.1);
|
|
});
|
|
it("EARTH_RADIUS_KM is 6371", () => {
|
|
assert.strictEqual(EARTH_RADIUS_KM, 6371);
|
|
});
|
|
});
|
|
|
|
describe("qiblaAngle", () => {
|
|
it("returns a number between 0 and 360", () => {
|
|
const angle = qiblaAngle(40.7128, -74.006);
|
|
assert.ok(angle >= 0 && angle < 360);
|
|
});
|
|
it("New York City (~58° NE)", () => {
|
|
const angle = qiblaAngle(40.7128, -74.006);
|
|
assert.ok(angle > 50 && angle < 70, `Expected 50-70, got ${angle}`);
|
|
});
|
|
it("London (~119° SE)", () => {
|
|
const angle = qiblaAngle(51.5074, -0.1278);
|
|
assert.ok(angle > 110 && angle < 130, `Expected 110-130, got ${angle}`);
|
|
});
|
|
it("Tokyo (~293° NW)", () => {
|
|
const angle = qiblaAngle(35.6762, 139.6503);
|
|
assert.ok(angle > 280 && angle < 310, `Expected 280-310, got ${angle}`);
|
|
});
|
|
it("Sydney (~277° W)", () => {
|
|
const angle = qiblaAngle(-33.8688, 151.2093);
|
|
assert.ok(angle > 260 && angle < 300, `Expected 260-300, got ${angle}`);
|
|
});
|
|
it("Islamabad (~268° W)", () => {
|
|
const angle = qiblaAngle(33.6844, 73.0479);
|
|
assert.ok(angle > 250 && angle < 290, `Expected 250-290, got ${angle}`);
|
|
});
|
|
it("returns finite number at Ka'bah (degenerate case)", () => {
|
|
const angle = qiblaAngle(KAABA_LAT, KAABA_LNG);
|
|
assert.ok(Number.isFinite(angle));
|
|
});
|
|
it("equator east of Mecca points NW", () => {
|
|
const angle = qiblaAngle(0, 80);
|
|
assert.ok(angle > 270 && angle < 360, `Expected 270-360, got ${angle}`);
|
|
});
|
|
it("result is stable (same input gives same output)", () => {
|
|
const a = qiblaAngle(40.7128, -74.006);
|
|
const b = qiblaAngle(40.7128, -74.006);
|
|
assert.strictEqual(a, b);
|
|
});
|
|
it("throws RangeError for invalid latitude", () => {
|
|
assert.throws(() => qiblaAngle(91, 0), RangeError);
|
|
assert.throws(() => qiblaAngle(-91, 0), RangeError);
|
|
});
|
|
it("throws RangeError for invalid longitude", () => {
|
|
assert.throws(() => qiblaAngle(0, 181), RangeError);
|
|
assert.throws(() => qiblaAngle(0, -181), RangeError);
|
|
});
|
|
});
|
|
|
|
describe("compassDir", () => {
|
|
it("returns N for 0°", () => assert.strictEqual(compassDir(0), "N"));
|
|
it("returns N for 360°", () => assert.strictEqual(compassDir(360), "N"));
|
|
it("returns NE for 45°", () => assert.strictEqual(compassDir(45), "NE"));
|
|
it("returns E for 90°", () => assert.strictEqual(compassDir(90), "E"));
|
|
it("returns SE for 135°", () => assert.strictEqual(compassDir(135), "SE"));
|
|
it("returns S for 180°", () => assert.strictEqual(compassDir(180), "S"));
|
|
it("returns SW for 225°", () => assert.strictEqual(compassDir(225), "SW"));
|
|
it("returns W for 270°", () => assert.strictEqual(compassDir(270), "W"));
|
|
it("returns NW for 315°", () => assert.strictEqual(compassDir(315), "NW"));
|
|
it("returns NE for NYC Qibla", () => {
|
|
const bearing = qiblaAngle(40.7128, -74.006);
|
|
assert.strictEqual(compassDir(bearing), "NE");
|
|
});
|
|
});
|
|
|
|
describe("compassName", () => {
|
|
it("returns North for 0°", () => assert.strictEqual(compassName(0), "North"));
|
|
it("returns Northeast for 45°", () =>
|
|
assert.strictEqual(compassName(45), "Northeast"));
|
|
it("returns East for 90°", () => assert.strictEqual(compassName(90), "East"));
|
|
it("returns Southeast for 135°", () =>
|
|
assert.strictEqual(compassName(135), "Southeast"));
|
|
it("returns South for 180°", () =>
|
|
assert.strictEqual(compassName(180), "South"));
|
|
it("returns Southwest for 225°", () =>
|
|
assert.strictEqual(compassName(225), "Southwest"));
|
|
it("returns West for 270°", () =>
|
|
assert.strictEqual(compassName(270), "West"));
|
|
it("returns Northwest for 315°", () =>
|
|
assert.strictEqual(compassName(315), "Northwest"));
|
|
it("returns North for 360°", () =>
|
|
assert.strictEqual(compassName(360), "North"));
|
|
});
|
|
|
|
describe("qiblaGreatCircle", () => {
|
|
it("returns an array of [lat, lng] pairs", () => {
|
|
const points = qiblaGreatCircle(40.7128, -74.006);
|
|
assert.ok(Array.isArray(points));
|
|
assert.ok(points.length > 0);
|
|
assert.strictEqual(points[0].length, 2);
|
|
});
|
|
it("returns 121 points by default", () => {
|
|
const points = qiblaGreatCircle(40.7128, -74.006);
|
|
assert.strictEqual(points.length, 121);
|
|
});
|
|
it("respects custom steps parameter", () => {
|
|
const points = qiblaGreatCircle(40.7128, -74.006, 60);
|
|
assert.strictEqual(points.length, 61);
|
|
});
|
|
it("first point is close to origin", () => {
|
|
const [lat, lng] = qiblaGreatCircle(40.7128, -74.006)[0];
|
|
assert.ok(Math.abs(lat - 40.7128) < 0.01);
|
|
assert.ok(Math.abs(lng - -74.006) < 0.01);
|
|
});
|
|
it("last point is close to Ka'bah", () => {
|
|
const points = qiblaGreatCircle(40.7128, -74.006);
|
|
const [lat, lng] = points[points.length - 1];
|
|
assert.ok(Math.abs(lat - KAABA_LAT) < 0.01);
|
|
assert.ok(Math.abs(lng - KAABA_LNG) < 0.01);
|
|
});
|
|
it("all points have valid coordinates", () => {
|
|
const points = qiblaGreatCircle(51.5074, -0.1278, 10);
|
|
for (const [lat, lng] of points) {
|
|
assert.ok(Number.isFinite(lat));
|
|
assert.ok(Number.isFinite(lng));
|
|
assert.ok(lat >= -90 && lat <= 90);
|
|
assert.ok(lng >= -180 && lng <= 180);
|
|
}
|
|
});
|
|
it("returns single point at Ka'bah", () => {
|
|
const points = qiblaGreatCircle(KAABA_LAT, KAABA_LNG);
|
|
assert.strictEqual(points.length, 1);
|
|
assert.ok(Math.abs(points[0][0] - KAABA_LAT) < 0.0001);
|
|
assert.ok(Math.abs(points[0][1] - KAABA_LNG) < 0.0001);
|
|
});
|
|
it("throws RangeError for invalid coordinates", () => {
|
|
assert.throws(() => qiblaGreatCircle(91, 0), RangeError);
|
|
assert.throws(() => qiblaGreatCircle(0, 181), RangeError);
|
|
});
|
|
});
|
|
|
|
describe("distanceKm", () => {
|
|
it("returns 0 for the same point", () => {
|
|
assert.ok(Math.abs(distanceKm(40.7128, -74.006, 40.7128, -74.006)) < 0.001);
|
|
});
|
|
it("NYC to Ka'bah is approximately 9600 km", () => {
|
|
const km = distanceKm(40.7128, -74.006, KAABA_LAT, KAABA_LNG);
|
|
assert.ok(km > 9000 && km < 10500, `Expected 9000-10500, got ${km}`);
|
|
});
|
|
it("London to Ka'bah is approximately 4950 km", () => {
|
|
const km = distanceKm(51.5074, -0.1278, KAABA_LAT, KAABA_LNG);
|
|
assert.ok(km > 4500 && km < 5500, `Expected 4500-5500, got ${km}`);
|
|
});
|
|
it("distance is symmetric", () => {
|
|
const d1 = distanceKm(40.7128, -74.006, KAABA_LAT, KAABA_LNG);
|
|
const d2 = distanceKm(KAABA_LAT, KAABA_LNG, 40.7128, -74.006);
|
|
assert.ok(Math.abs(d1 - d2) < 0.001);
|
|
});
|
|
it("quarter equator is approximately 10,018 km", () => {
|
|
const d = distanceKm(0, 0, 0, 90);
|
|
assert.ok(d > 9800 && d < 10200, `Expected 9800-10200, got ${d}`);
|
|
});
|
|
it("pole to pole is approximately 20,000 km", () => {
|
|
const d = distanceKm(90, 0, -90, 0);
|
|
assert.ok(d > 19000 && d < 21000, `Expected 19000-21000, got ${d}`);
|
|
});
|
|
it("returns positive for distinct points", () => {
|
|
assert.ok(distanceKm(0, 0, 10, 10) > 0);
|
|
});
|
|
});
|