Skip to content

Commit

Permalink
create-daml-app: make tests pass on m1 (#15031) (#15077)
Browse files Browse the repository at this point in the history
This is very much a "just make it work" type of patch. The main issues
were:

- A bunch of type errors in the `index.test.ts` file when opened in VS
  Code. I'm not sure how it happened, but it's possible I'm just using a
  more recent TS compiler than whoever edited this last.
- `localhost`, on my machine at least, resolves to `::1`, and therefore
  the connections were just not established. I've opted for replacing
  `localhost` with explicit `127.0.0.1`.
- `npm-cli.js` was not an executable in my `PATH`, and won't be for
  most users, but `npm` is not on our testing infrastructure's `PATH`.

Those tests are obviously not tested by CI, so please review carefully /
test this on your own machine before approving.

CHANGELOG_BEGIN
CHANGELOG_END
  • Loading branch information
garyverhaegen-da committed Sep 27, 2022
1 parent f7397f6 commit 7e202ea
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 76 deletions.
2 changes: 1 addition & 1 deletion ci/configure-bazel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ if is_windows; then
SUFFIX="${SUFFIX:0:12}"
echo "Platform suffix: $SUFFIX"
# We include an extra version at the end that we can bump manually.
CACHE_SUFFIX="$SUFFIX-v12"
CACHE_SUFFIX="$SUFFIX-v13"
CACHE_URL="$CACHE_URL/$CACHE_SUFFIX"
echo "build:windows-ci --remote_http_cache=https://bazel-cache.da-ext.net/$CACHE_SUFFIX" >> .bazelrc.local
fi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ tests =
testFileContent <- T.readFileUtf8 testFile
T.writeFileUtf8
(uiDir </> "src" </> "index.test.ts")
(T.replace "create-daml-app" (T.pack projectName) testFileContent)
(T.replace "\"npm\"" "\"npm-cli.js\"" (T.replace "create-daml-app" (T.pack projectName) testFileContent))
-- patch daml.yaml, remove JavaScript code generation entry so that patched generated code
-- is not overwritten
let damlYaml = cdaDir </> "daml.yaml"
Expand Down
107 changes: 51 additions & 56 deletions templates/create-daml-app-test-resources/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { promises as fs } from "fs";
import puppeteer, { Browser, Page } from "puppeteer";
import waitOn from "wait-on";

import Ledger, { UserRightHelper } from "@daml/ledger";
import Ledger, { UserRightHelper, UserRight } from "@daml/ledger";
import { User } from "@daml.js/create-daml-app";
import { authConfig } from "./config";
import { insecure } from "./config";

const JSON_API_PORT_FILE_NAME = "json-api.port";

Expand All @@ -29,15 +29,16 @@ let publicUser: string | undefined;
let publicParty: string | undefined;

const adminLedger = new Ledger({
token: authConfig.makeToken("participant_admin"),
token: insecure.makeToken("participant_admin"),
httpBaseUrl: "http://127.0.0.1:7575/",
});

const toAlias = (userId: string): string =>
userId.charAt(0).toUpperCase() + userId.slice(1);

// Function to generate unique party names for us.
let nextPartyId = 1;
const getParty = async (): [string, string] => {
const getParty = async (): Promise<[string, string]> => {
const allocResult = await adminLedger.allocateParty({});
const user = `u${nextPartyId}`;
const party = allocResult.identifier;
Expand All @@ -50,7 +51,7 @@ const getParty = async (): [string, string] => {
};

test("Party names are unique", async () => {
let r = [];
let r: string[] = [];
for (let i = 0; i < 10; ++i) {
r = r.concat((await getParty())[1]);
}
Expand All @@ -71,35 +72,19 @@ const removeFile = async (path: string) => {
// To reduce test times, we reuse the same processes between all the tests.
// This means we need to use a different set of parties and a new browser page for each test.
beforeAll(async () => {
// If the JSON API server was previously shut down abruptly then the port file
// may not have been removed.
// Since we use this file to know when the server is up, we remove it first
// (if it exists) to be sure.
const jsonApiPortFilePath = `../${JSON_API_PORT_FILE_NAME}`; // relative to ui folder
await removeFile(jsonApiPortFilePath);

// Run `daml start` from the project root (where the `daml.yaml` is located).
// The path should include '.daml/bin' in the environment where this is run,
// which contains the `daml` assistant executable.
const startOpts: SpawnOptions = { cwd: "..", stdio: "inherit" };

// Arguments for `daml start` (besides those in the `daml.yaml`).
// The JSON API `--port-file` gives us a file we can check to know that both
// the sandbox and JSON API server are up and running.
// We use the default ports for the sandbox and JSON API as done in the
// Getting Started Guide.
const startArgs = [
"start",
`--json-api-option=--port-file=${JSON_API_PORT_FILE_NAME}`,
];

console.debug("Starting daml start");

startProc = spawn("daml", startArgs, startOpts);

await waitOn({ resources: [`file:${jsonApiPortFilePath}`] });
startProc = spawn("daml", ["start"], startOpts);

console.debug("daml start API are running");
await waitOn({ resources: [`tcp:127.0.0.1:6865`] });
console.debug("daml sandbox is running");
await waitOn({ resources: [`tcp:127.0.0.1:7575`] });
console.debug("JSON API is running");

[publicUser, publicParty] = await getParty();

Expand All @@ -108,7 +93,7 @@ beforeAll(async () => {
// https://github.com/facebook/create-react-app/issues/873#issuecomment-266318338
const env = { ...process.env, BROWSER: "none" };
console.debug("Starting npm start");
uiProc = spawn("npm-cli.js", ["run-script", "start"], {
uiProc = spawn("npm", ["start"], {
env,
stdio: "inherit",
detached: true,
Expand All @@ -118,7 +103,7 @@ beforeAll(async () => {
// following https://azimi.me/2014/12/31/kill-child_process-node-js.html.

// Ensure the UI server is ready by checking that the port is available.
await waitOn({ resources: [`tcp:localhost:${UI_PORT}`] });
await waitOn({ resources: [`tcp:127.0.0.1:${UI_PORT}`] });
console.debug("npm start is running");

// Launch a single browser for all tests.
Expand All @@ -140,7 +125,7 @@ afterAll(async () => {
// The `-` indicates to kill all processes in the process group.
// See Note(kill-npm-start).
// TODO: Test this on Windows.
if (uiProc) {
if (uiProc && uiProc.pid) {
process.kill(-uiProc.pid);
}

Expand All @@ -151,7 +136,7 @@ afterAll(async () => {

test("create and look up user using ledger library", async () => {
const [user, party] = await getParty();
const token = authConfig.makeToken(user);
const token = insecure.makeToken(user);
const ledger = new Ledger({ token });
const users0 = await ledger.query(User.User);
expect(users0).toEqual([]);
Expand Down Expand Up @@ -203,10 +188,12 @@ const login = async (page: Page, partyName: string) => {
const usernameInput = await page.waitForSelector(
".test-select-username-field",
);
await usernameInput.click();
await usernameInput.type(partyName);
await page.click(".test-select-login-button");
await page.waitForSelector(".test-select-main-menu");
if (usernameInput) {
await usernameInput.click();
await usernameInput.type(partyName);
await page.click(".test-select-login-button");
await page.waitForSelector(".test-select-main-menu");
}
};
// LOGIN_FUNCTION_END

Expand All @@ -219,19 +206,21 @@ const logout = async (page: Page) => {
// Follow a user using the text input in the follow panel.
const follow = async (page: Page, userToFollow: string) => {
const followInput = await page.waitForSelector(".test-select-follow-input");
await followInput.click();
await followInput.type(userToFollow);
await followInput.press("Enter");
await page.click(".test-select-follow-button");

// Wait for the request to complete, either successfully or after the error
// dialog has been handled.
// We check this by the absence of the `loading` class.
// (Both the `test-...` and `loading` classes appear in `div`s surrounding
// the `input`, due to the translation of Semantic UI's `Input` element.)
await page.waitForSelector(".test-select-follow-input > :not(.loading)", {
timeout: 40_000,
});
if (followInput) {
await followInput.click();
await followInput.type(userToFollow);
await followInput.press("Enter");
await page.click(".test-select-follow-button");

// Wait for the request to complete, either successfully or after the error
// dialog has been handled.
// We check this by the absence of the `loading` class.
// (Both the `test-...` and `loading` classes appear in `div`s surrounding
// the `input`, due to the translation of Semantic UI's `Input` element.)
await page.waitForSelector(".test-select-follow-input > :not(.loading)", {
timeout: 40_000,
});
}
};

// LOGIN_TEST_BEGIN
Expand All @@ -243,7 +232,7 @@ test("log in as a new user, log out and log back in", async () => {
await login(page, user);

// Check that the ledger contains the new User contract.
const token = authConfig.makeToken(user);
const token = insecure.makeToken(user);
const ledger = new Ledger({ token });
const users = await ledger.query(User.User);
expect(users).toHaveLength(1);
Expand Down Expand Up @@ -419,14 +408,20 @@ const failedLogin = async (page: Page, partyName: string) => {
const usernameInput = await page.waitForSelector(
".test-select-username-field",
);
await usernameInput.click();
await usernameInput.type(partyName);
await page.click(".test-select-login-button");
await page.waitForFunction(
async () => (await window.getError()) !== undefined,
);
expect(dismissError).toHaveBeenCalled();
return error;
if (usernameInput) {
await usernameInput.click();
await usernameInput.type(partyName);
await page.click(".test-select-login-button");
await page.waitForFunction(
// Casting window as any so the TS compiler doesn't flag this as an
// error.
// The window object normally doesn't have a .getError method, but
// we're adding one above with exposeFunction.
async () => (await (window as any).getError()) !== undefined,
);
expect(dismissError).toHaveBeenCalled();
return error;
}
};

test("error on user id with invalid format", async () => {
Expand Down
35 changes: 17 additions & 18 deletions templates/create-daml-app/ui/src/config.ts.template
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,21 @@ export const userManagement: UserManagement =
// isn’t explicitly "false" as supporting user management.
process.env.REACT_APP_SUPPORTS_USERMANAGEMENT?.toLowerCase() !== "false" ? withUserManagement : noUserManagement;

export const authConfig: Authentication = (() => {
if (isRunningOnHub()) {
const auth: DamlHub = {
provider: "daml-hub",
};
return auth;
} else {
const ledgerId: string = process.env.REACT_APP_LEDGER_ID ?? "__PROJECT_NAME__-sandbox"
const auth: Insecure = {
provider: "none",
userManagement: userManagement,
makeToken: (loginName) => {
const payload = userManagement.tokenPayload(loginName, ledgerId);
return encode(payload, "secret", "HS256");
}
};
return auth;
}
export const damlHub: DamlHub = {
provider: "daml-hub",
};

export const insecure: Insecure = (() => {
const ledgerId: string = process.env.REACT_APP_LEDGER_ID ?? "__PROJECT_NAME__-sandbox";
return {
provider: "none" as "none",
userManagement: userManagement,
makeToken: (loginName: string) => {
const payload = userManagement.tokenPayload(loginName, ledgerId);
return encode(payload, "secret", "HS256");
}
};
})();

export const authConfig: Authentication = (() =>
isRunningOnHub() ? damlHub : insecure)();

0 comments on commit 7e202ea

Please sign in to comment.