Attempt to set up tests that work with auth

main
jaxoncreed 2 years ago
parent 8c258f3ede
commit 92a9a57f28
  1. 1400
      package-lock.json
  2. 12
      packages/solid-react/jest.config.js
  3. 2
      packages/solid-react/jest.setup.ts
  4. 3
      packages/solid-react/package.json
  5. 10
      packages/solid-react/src/useLdoMethods.ts
  6. 41
      packages/solid-react/test/BrowserAuthentication.test.tsx
  7. 7
      packages/solid-react/test/configs/solid-css-seed.json
  8. 14
      packages/solid-react/test/environment/customEnvironment.ts
  9. 128
      packages/solid-react/test/setUpServer.ts
  10. 122
      packages/solid-react/test/solidServer.helper.ts
  11. 5
      packages/solid-react/test/trivial.test.ts
  12. 3
      packages/solid-react/tsconfig.build.json
  13. 60
      packages/solid/src/package.json
  14. 3
      tsconfig.json

1400
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,5 +1,11 @@
const sharedConfig = require('../../jest.config.js');
const sharedConfig = require("../../jest.config.js");
module.exports = {
...sharedConfig,
'rootDir': './',
}
preset: "ts-jest/presets/js-with-ts",
testEnvironment: "jsdom",
rootDir: "./",
transformIgnorePatterns: ["undici"],
injectGlobals: true,
testEnvironment: "<rootDir>/test/environment/customEnvironment.ts",
setupFilesAfterEnv: ["<rootDir>/jest.setup.ts"],
};

@ -0,0 +1,2 @@
import "@inrupt/jest-jsdom-polyfills";
globalThis.fetch = async () => new Response();

@ -23,10 +23,13 @@
},
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid-react#readme",
"devDependencies": {
"@inrupt/jest-jsdom-polyfills": "^3.0.2",
"@inrupt/solid-client-authn-core": "^1.17.1",
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@rdfjs/types": "^1.0.1",
"@testing-library/react": "^14.1.2",
"@types/jest": "^29.0.3",
"jest-environment-jsdom": "^29.7.0",
"ts-jest": "^29.0.2"
},
"dependencies": {

@ -48,9 +48,15 @@ export function createUseLdoMethods(dataset: SolidLdoDataset): UseLdoMethods {
createData<Type extends LdoBase>(
shapeType: ShapeType<Type>,
subject: string | SubjectNode,
...resources: Resource[]
resource: Resource,
...additionalResources: Resource[]
): Type {
return dataset.createData(shapeType, subject, ...resources);
return dataset.createData(
shapeType,
subject,
resource,
...additionalResources,
);
},
/**
* Begins tracking changes to eventually commit

@ -0,0 +1,41 @@
import React from "react";
import type { FunctionComponent } from "react";
import { fireEvent, render, screen } from "@testing-library/react";
import { setUpServer } from "./setUpServer";
import { useSolidAuth } from "../src/SolidAuthContext";
import { ROOT_CONTAINER } from "./solidServer.helper";
import { BrowserSolidLdoProvider } from "../src/BrowserSolidLdoProvider";
describe("Browser Authentication", () => {
const s = setUpServer();
const AuthTest: FunctionComponent = () => {
const { login, session, logout } = useSolidAuth();
return (
<div>
<div data-testid="session">{JSON.stringify(session)}</div>
<button onClick={() => login(ROOT_CONTAINER)} aria-label="login">
Login
</button>
<button onClick={logout} aria-label="logout">
LogOut
</button>
</div>
);
};
it("properly logs in", async () => {
render(
<BrowserSolidLdoProvider>
<AuthTest />
</BrowserSolidLdoProvider>,
);
const loginButton = screen.getByRole("button", { name: "logout" });
fireEvent.click(loginButton);
await screen.findByText("Log in");
expect(window.location.pathname).toBe("/.account/login/password/");
// const authorizeButton = screen.getByText("Log in");
// const emailBox = screen.getById
});
});

@ -0,0 +1,7 @@
[
{
"podName": "example",
"email": "hello@example.com",
"password": "abc123"
}
]

@ -0,0 +1,14 @@
import Environment from "jest-environment-jsdom";
export default class CustomTestEnvironment extends Environment {
async setup() {
await super.setup();
if (typeof this.global.TextEncoder === "undefined") {
// The following doesn't work from jest-jsdom-polyfills.
// TextEncoder (global or via 'util') references a Uint8Array constructor
// different than the global one used by users in tests. This makes sure the
// same constructor is referenced by both.
this.global.Uint8Array = Uint8Array;
}
}
}

@ -0,0 +1,128 @@
import type { ContainerUri, LeafUri } from "@ldo/solid";
import {
ROOT_CONTAINER,
createApp,
getAuthenticatedFetch,
} from "./solidServer.helper";
import type { App } from "@solid/community-server";
export const TEST_CONTAINER_SLUG = "test_ldo/";
export const TEST_CONTAINER_URI =
`${ROOT_CONTAINER}${TEST_CONTAINER_SLUG}` as ContainerUri;
export const SAMPLE_DATA_URI = `${TEST_CONTAINER_URI}sample.ttl` as LeafUri;
export const SAMPLE2_DATA_SLUG = "sample2.ttl";
export const SAMPLE2_DATA_URI =
`${TEST_CONTAINER_URI}${SAMPLE2_DATA_SLUG}` as LeafUri;
export const SAMPLE_BINARY_URI = `${TEST_CONTAINER_URI}sample.txt` as LeafUri;
export const SAMPLE2_BINARY_SLUG = `sample2.txt`;
export const SAMPLE2_BINARY_URI =
`${TEST_CONTAINER_URI}${SAMPLE2_BINARY_SLUG}` as LeafUri;
export const SAMPLE_CONTAINER_URI =
`${TEST_CONTAINER_URI}sample_container/` as ContainerUri;
export const SPIDER_MAN_TTL = `@base <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix rel: <http://www.perceive.net/schemas/relationship/> .
<#green-goblin>
rel:enemyOf <#spiderman> ;
a foaf:Person ; # in the context of the Marvel universe
foaf:name "Green Goblin" .
<#spiderman>
rel:enemyOf <#green-goblin> ;
a foaf:Person ;
foaf:name "Spiderman", "Человек-паук"@ru .`;
export const TEST_CONTAINER_TTL = `@prefix dc: <http://purl.org/dc/terms/>.
@prefix ldp: <http://www.w3.org/ns/ldp#>.
@prefix posix: <http://www.w3.org/ns/posix/stat#>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
<> <urn:npm:solid:community-server:http:slug> "sample.txt";
a ldp:Container, ldp:BasicContainer, ldp:Resource;
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime.
<sample.ttl> a ldp:Resource, <http://www.w3.org/ns/iana/media-types/text/turtle#Resource>;
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime.
<sample.txt> a ldp:Resource, <http://www.w3.org/ns/iana/media-types/text/plain#Resource>;
dc:modified "2023-10-20T13:57:14.000Z"^^xsd:dateTime.
<> posix:mtime 1697810234;
ldp:contains <sample.ttl>, <sample.txt>.
<sample.ttl> posix:mtime 1697810234;
posix:size 522.
<sample.txt> posix:mtime 1697810234;
posix:size 10.`;
export interface SetUpServerReturn {
app: App;
authFetch: typeof fetch;
fetchMock: jest.Mock<
Promise<Response>,
[input: RequestInfo | URL, init?: RequestInit | undefined]
>;
}
export function setUpServer(): SetUpServerReturn {
// Ignore to build s
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const s: SetUpServerReturn = {};
beforeAll(async () => {
// Start up the server
s.app = await createApp();
await s.app.start();
s.authFetch = await getAuthenticatedFetch();
});
afterAll(async () => {
s.app.stop();
});
beforeEach(async () => {
s.fetchMock = jest.fn(s.authFetch);
// Create a new document called sample.ttl
await s.authFetch(ROOT_CONTAINER, {
method: "POST",
headers: {
link: '<http://www.w3.org/ns/ldp#Container>; rel="type"',
slug: TEST_CONTAINER_SLUG,
},
});
await Promise.all([
s.authFetch(TEST_CONTAINER_URI, {
method: "POST",
headers: { "content-type": "text/turtle", slug: "sample.ttl" },
body: SPIDER_MAN_TTL,
}),
s.authFetch(TEST_CONTAINER_URI, {
method: "POST",
headers: { "content-type": "text/plain", slug: "sample.txt" },
body: "some text.",
}),
]);
});
afterEach(async () => {
await Promise.all([
s.authFetch(SAMPLE_DATA_URI, {
method: "DELETE",
}),
s.authFetch(SAMPLE2_DATA_URI, {
method: "DELETE",
}),
s.authFetch(SAMPLE_BINARY_URI, {
method: "DELETE",
}),
s.authFetch(SAMPLE2_BINARY_URI, {
method: "DELETE",
}),
s.authFetch(SAMPLE_CONTAINER_URI, {
method: "DELETE",
}),
]);
});
return s;
}

@ -0,0 +1,122 @@
// Taken from https://github.com/comunica/comunica/blob/b237be4265c353a62a876187d9e21e3bc05123a3/engines/query-sparql/test/QuerySparql-solid-test.ts#L9
import * as path from "path";
import type { KeyPair } from "@inrupt/solid-client-authn-core";
import {
buildAuthenticatedFetch,
createDpopHeader,
generateDpopKeyPair,
} from "@inrupt/solid-client-authn-core";
import type { App } from "@solid/community-server";
import { AppRunner, resolveModulePath } from "@solid/community-server";
import "jest-rdf";
import fetch from "cross-fetch";
const config = [
{
podName: process.env.USER_NAME || "example",
email: process.env.EMAIL || "hello@example.com",
password: process.env.PASSWORD || "abc123",
},
];
export const SERVER_DOMAIN = process.env.SERVER || "http://localhost:3001/";
export const ROOT_ROUTE = process.env.ROOT_CONTAINER || "example/";
export const ROOT_CONTAINER = `${SERVER_DOMAIN}${ROOT_ROUTE}`;
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);
export async function createApp(): Promise<App> {
if (process.env.SERVER) {
return {
start: () => {},
stop: () => {},
} as App;
}
const appRunner = new AppRunner();
return appRunner.create(
{
mainModulePath: resolveModulePath(""),
typeChecking: false,
},
resolveModulePath("config/default.json"),
{},
{
port: 3_001,
loggingLevel: "off",
seededPodConfigJson: path.join(
__dirname,
"configs",
"solid-css-seed.json",
),
},
);
}
export interface ISecretData {
id: string;
secret: string;
}
// From https://communitysolidserver.github.io/CommunitySolidServer/5.x/usage/client-credentials/
export async function getSecret(): Promise<ISecretData> {
const result = await fetch(`${SERVER_DOMAIN}idp/credentials/`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
email: config[0].email,
password: config[0].password,
name: config[0].podName,
}),
});
const json = await result.json();
return json;
}
export interface ITokenData {
accessToken: string;
dpopKey: KeyPair;
}
// From https://communitysolidserver.github.io/CommunitySolidServer/5.x/usage/client-credentials/
export async function refreshToken({
id,
secret,
}: ISecretData): Promise<ITokenData> {
const dpopKey = await generateDpopKeyPair();
const authString = `${encodeURIComponent(id)}:${encodeURIComponent(secret)}`;
const tokenUrl = `${SERVER_DOMAIN}.oidc/token`;
const accessToken = await fetch(tokenUrl, {
method: "POST",
headers: {
// The header needs to be in base64 encoding.
authorization: `Basic ${Buffer.from(authString).toString("base64")}`,
"content-type": "application/x-www-form-urlencoded",
dpop: await createDpopHeader(tokenUrl, "POST", dpopKey),
},
body: "grant_type=client_credentials&scope=webid",
})
.then((res) => res.json())
.then((res) => res.access_token);
return { accessToken, dpopKey };
}
export async function getAuthenticatedFetch() {
// Generate secret
const secret = await getSecret();
if (!secret) throw new Error("No Secret");
// Get token
const token = await refreshToken(secret);
if (!token) throw new Error("No Token");
// Build authenticated fetch
const authFetch = await buildAuthenticatedFetch(fetch, token.accessToken, {
dpopKey: token.dpopKey,
});
return authFetch;
}

@ -1,5 +0,0 @@
describe("Trivial", () => {
it("Trivial", () => {
expect(true).toBe(true);
});
});

@ -2,7 +2,8 @@
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"outDir": "./dist",
"lib": ["dom"]
"lib": ["dom"],
"allowJs": true
},
"include": ["./src"]
}

@ -1,60 +0,0 @@
{
"name": "@ldo/solid",
"version": "0.0.1-alpha.17",
"description": "A library for LDO and Solid",
"main": "dist/index.js",
"scripts": {
"example": "ts-node ./example/example.ts",
"build": "tsc --project tsconfig.build.json",
"watch": "tsc --watch",
"test": "jest --coverage",
"test:watch": "jest --watch",
"prepublishOnly": "npm run test && npm run build",
"build:ldo": "ldo build --input src/.shapes --output src/.ldo",
"lint": "eslint src/** --fix --no-error-on-unmatched-pattern",
"docs": "typedoc --plugin typedoc-plugin-markdown"
},
"repository": {
"type": "git",
"url": "git+https://github.com/o-development/ldobjects.git"
},
"author": "Jackson Morgan",
"license": "MIT",
"bugs": {
"url": "https://github.com/o-development/ldobjects/issues"
},
"homepage": "https://github.com/o-development/ldobjects/tree/main/packages/solid#readme",
"devDependencies": {
"@inrupt/solid-client-authn-core": "^1.17.1",
"@ldo/cli": "^0.0.1-alpha.17",
"@rdfjs/data-model": "^1.2.0",
"@rdfjs/types": "^1.0.1",
"@solid/community-server": "^6.0.2",
"@types/jest": "^29.0.3",
"dotenv": "^16.3.1",
"jest-rdf": "^1.8.0",
"ts-jest": "^29.0.2",
"ts-node": "^10.9.1",
"typed-emitter": "^2.1.0",
"typedoc": "^0.25.4",
"typedoc-plugin-markdown": "^3.17.1"
},
"dependencies": {
"@inrupt/solid-client": "^1.30.0",
"@ldo/dataset": "^0.0.1-alpha.17",
"@ldo/ldo": "^0.0.1-alpha.17",
"@ldo/rdf-utils": "^0.0.1-alpha.17",
"@types/parse-link-header": "^2.0.1",
"cross-fetch": "^3.1.6",
"http-link-header": "^1.1.1",
"ts-mixer": "^6.0.3"
},
"files": [
"dist",
"src"
],
"publishConfig": {
"access": "public"
},
"gitHead": "1c242d645fc488f8d0e9f4da7425d9928abf1e9d"
}

@ -3,6 +3,7 @@
"compilerOptions": {
"paths": {
"@ldo/*": ["packages/*/src"]
}
},
"allowJs": true
}
}
Loading…
Cancel
Save