-
Notifications
You must be signed in to change notification settings - Fork 16
fix(apps/ensadmin): validate name query params on name details page #1834
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4bf234f
f7893a2
569f803
255190e
0a17eea
0ab0331
14e162f
72e310e
1f29be9
a205294
1fd70f1
2ff21a5
3c05ce3
bb498a4
077628b
3308ed9
e92eb03
310919a
f2c0398
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "ensadmin": patch | ||
| --- | ||
|
|
||
| Add input validation and normalization to the Explore Names form and name detail page. The form uses `interpretNameFromUserInput` to normalize valid inputs before navigating, and shows inline errors for invalid or unsupported names. The detail page validates query params with `isNormalizedName`/`isInterpretedName` and shows appropriate error states. Empty name params show the form instead of a broken detail page. |
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm aware this is a new file, and not sure where this lives long term. |
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,20 @@ | ||||||||||||
| import { ErrorInfo } from "@/components/error-info"; | ||||||||||||
|
|
||||||||||||
| export function UnnormalizedNameError() { | ||||||||||||
| return ( | ||||||||||||
| <section className="p-6"> | ||||||||||||
| <ErrorInfo title="Invalid Name" description="The provided name is not a valid ENS name." /> | ||||||||||||
|
||||||||||||
| <ErrorInfo title="Invalid Name" description="The provided name is not a valid ENS name." /> | |
| <ErrorInfo | |
| title="Unnormalized Name" | |
| description="The provided name is not in normalized ENS form. Please use a normalized ENS name (for example, lowercase and properly formatted)." | |
| /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧹 Nitpick | 🔵 Trivial
Consider displaying the interpreted name in the error message.
Since InterpretedNameUnsupportedError is shown for valid InterpretedName values (with encoded labelhashes), you could accept a name: InterpretedName prop and display it using formatInterpretedNameForDisplay for better user context. This aligns with past review feedback suggesting <InterpretedNameUnsupportedError name={...} />.
💡 Optional enhancement
+import type { InterpretedName } from "@ensnode/ensnode-sdk";
+import { formatInterpretedNameForDisplay } from "@/lib/format-interpreted-name-for-display";
import { ErrorInfo } from "@/components/error-info";
-export function InterpretedNameUnsupportedError() {
+export function InterpretedNameUnsupportedError({ name }: { name: InterpretedName }) {
return (
<section className="p-6">
<ErrorInfo
title="Encoded Labelhash Detected"
- description="The provided name contains encoded labelhashes. Support for resolving names with encoded labelhashes is in progress and coming soon."
+ description={`The name "${formatInterpretedNameForDisplay(name)}" contains encoded labelhashes. Support for resolving names with encoded labelhashes is in progress and coming soon.`}
/>
</section>
);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/ensadmin/src/app/name/_components/NameErrors.tsx` around lines 11 - 20,
Update the InterpretedNameUnsupportedError component to accept a prop name of
type InterpretedName and show the interpreted name in the error description
using formatInterpretedNameForDisplay; specifically, change the
InterpretedNameUnsupportedError signature to take { name: InterpretedName },
call formatInterpretedNameForDisplay(name) and include that string in the
ErrorInfo description (or add a separate field) so the rendered message shows
the actual interpreted name for context.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,14 +2,14 @@ | |
|
|
||
| import { EnsAvatar, NameDisplay } from "@namehash/namehash-ui"; | ||
|
|
||
| import type { ENSNamespaceId, Name } from "@ensnode/ensnode-sdk"; | ||
| import type { ENSNamespaceId, NormalizedName } from "@ensnode/ensnode-sdk"; | ||
|
|
||
| import { ExternalLinkWithIcon } from "@/components/link"; | ||
| import { Card, CardContent } from "@/components/ui/card"; | ||
| import { beautifyUrl } from "@/lib/beautify-url"; | ||
|
|
||
| interface ProfileHeaderProps { | ||
| name: Name; | ||
| name: NormalizedName; | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was changed because of the comments made about the downstream handling, and:
|
||
| namespaceId: ENSNamespaceId; | ||
| headerImage?: string | null; | ||
| websiteUrl?: string | null; | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,8 +7,11 @@ import { type ChangeEvent, useMemo, useState } from "react"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { ENSNamespaceIds } from "@ensnode/datasources"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| getNamespaceSpecificValue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isInterpretedName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isNormalizedName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type Name, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type NamespaceSpecificValue, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type NormalizedName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@ensnode/ensnode-sdk"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
coderabbitai[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getNameDetailsRelativePath, NameLink } from "@/components/name-links"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -17,10 +20,15 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Input } from "@/components/ui/input"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useActiveNamespace } from "@/hooks/active/use-active-namespace"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { useRawConnectionUrlParam } from "@/hooks/use-connection-url-param"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interpretNameFromUserInput, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NameInterpretationOutcomeResult, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "@/lib/interpret-name-from-user-input"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { NameDetailPageContent } from "./_components/NameDetailPageContent"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { InterpretedNameUnsupportedError, UnnormalizedNameError } from "./_components/NameErrors"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const EXAMPLE_NAMES: NamespaceSpecificValue<string[]> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const EXAMPLE_NAMES: NamespaceSpecificValue<NormalizedName[]> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "vitalik.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "gregskril.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -34,15 +42,15 @@ const EXAMPLE_NAMES: NamespaceSpecificValue<string[]> = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "lens.xyz", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "brantly.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "lightwalker.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] as NormalizedName[], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ENSNamespaceIds.Sepolia]: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "gregskril.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "vitalik.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "myens.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "recordstest.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "arrondesean.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "decode.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] as NormalizedName[], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| [ENSNamespaceIds.EnsTestEnv]: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "alias.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "changerole.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -56,14 +64,15 @@ const EXAMPLE_NAMES: NamespaceSpecificValue<string[]> = { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "sub2.parent.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "test.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "wallet.linked.parent.eth", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] as NormalizedName[], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export default function ExploreNamesPage() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const router = useRouter(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const searchParams = useSearchParams(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const nameFromQuery = searchParams.get("name"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const nameFromQuery = searchParams.get("name") as Name | null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const nameFromQuery = searchParams.get("name") as Name | null; | |
| const rawNameFromQuery = searchParams.get("name"); | |
| const nameFromQuery = | |
| rawNameFromQuery && rawNameFromQuery.trim() !== "" | |
| ? (rawNameFromQuery.trim() as Name) | |
| : null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to do more work here to clean this up, and move it.
Copilot
AI
Mar 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whitespace-only input is treated as Empty (no-op) and the submit button is disabled based on rawInputName.trim(). That means an input like " " can't be submitted via the button and won't surface an "Invalid Name" error, which conflicts with issue #1140's acceptance criteria (it explicitly lists " " as an Invalid Name case). Consider disabling only for the truly empty string, and returning an invalid-name outcome (with an inline error) for whitespace-only input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // Detail page: validate name from query params using only validation checks (no normalization). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // see: https://github.com/namehash/ensnode/issues/1140 |
Done, I can remove these TODos now.
Copilot
AI
Mar 30, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The detail-page query-param validation currently treats a valid-but-unnormalized name (e.g. "VITALIK.ETH") the same as an invalid name and renders UnnormalizedNameError (with a "not a valid ENS name" message). This doesn’t match the intended behavior in #1140/#1059, where unnormalized-but-normalizable names should show an explicit "not ENS normalized" error distinct from truly invalid input. Consider adding a separate branch to detect “normalizable but not normalized” (e.g. via normalize(nameFromQuery) in a try/catch) and render a dedicated error state/message for that case.
| return <UnnormalizedNameError />; | |
| // Distinguish between names that are normalizable-but-unnormalized and | |
| // inputs that are truly invalid. | |
| const interpretationResult = interpretNameFromUserInput(nameFromQuery); | |
| if (interpretationResult.outcome === NameInterpretationOutcomeResult.Normalized) { | |
| // The name can be normalized but isn't in normalized form yet. | |
| // Show a dedicated "not ENS normalized" error state. | |
| return <UnnormalizedNameError />; | |
| } | |
| // For all other outcomes, treat the input as invalid and show a | |
| // "not a valid ENS name" message. | |
| return ( | |
| <section className="flex flex-col gap-6 p-6"> | |
| <Card className="w-full"> | |
| <CardHeader className="sm:pb-4 max-sm:p-3"> | |
| <CardTitle className="text-2xl">Explore ENS Names</CardTitle> | |
| </CardHeader> | |
| <CardContent className="max-sm:px-3 max-sm:pb-3"> | |
| <p className="text-sm text-red-600"> | |
| The provided input is not a valid ENS name. | |
| </p> | |
| </CardContent> | |
| </Card> | |
| </section> | |
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lightwalker-eth I added trim() here because I was seeing the View Profile button become active, and I think it's better it remains disabled. Empty characters are validated again but the disabled button is only checking for length.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@notrab In my mind we make "Empty" a special class of the interpretation outcome and then build logic such as disabling a "View Profile" button based on the "Empty" class of interpretation outcomes.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| import { describe, expect, it } from "vitest"; | ||
|
|
||
| import { | ||
| interpretNameFromUserInput, | ||
| NameInterpretationOutcomeResult, | ||
| } from "./interpret-name-from-user-input"; | ||
|
|
||
| describe("interpretNameFromUserInput", () => { | ||
| describe("Empty outcome", () => { | ||
| it("returns Empty for empty string", () => { | ||
| const result = interpretNameFromUserInput(""); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Empty); | ||
| expect(result.interpretation).toBe(""); | ||
| }); | ||
|
|
||
| it("returns Empty for whitespace-only string", () => { | ||
| const result = interpretNameFromUserInput(" "); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Empty); | ||
| expect(result.interpretation).toBe(""); | ||
| }); | ||
|
|
||
| it("returns Empty for tab/newline whitespace", () => { | ||
| const result = interpretNameFromUserInput("\t\n"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Empty); | ||
| expect(result.interpretation).toBe(""); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Normalized outcome", () => { | ||
| it("returns Normalized for already-normalized name", () => { | ||
| const result = interpretNameFromUserInput("vitalik.eth"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Normalized); | ||
| expect(result.interpretation).toBe("vitalik.eth"); | ||
| }); | ||
|
|
||
| it("normalizes uppercase to lowercase", () => { | ||
| const result = interpretNameFromUserInput("VITALIK.ETH"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Normalized); | ||
| expect(result.interpretation).toBe("vitalik.eth"); | ||
| }); | ||
|
|
||
| it("normalizes mixed case", () => { | ||
| const result = interpretNameFromUserInput("LiGhTWaLkEr.EtH"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Normalized); | ||
| expect(result.interpretation).toBe("lightwalker.eth"); | ||
| }); | ||
|
|
||
| it("preserves inputName in result", () => { | ||
| const result = interpretNameFromUserInput("VITALIK.ETH"); | ||
| expect(result.inputName).toBe("VITALIK.ETH"); | ||
| }); | ||
|
|
||
| it("handles single-label name", () => { | ||
| const result = interpretNameFromUserInput("eth"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Normalized); | ||
| expect(result.interpretation).toBe("eth"); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Reencoded outcome", () => { | ||
| it("returns Reencoded for encoded labelhash input", () => { | ||
| const result = interpretNameFromUserInput( | ||
| "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth", | ||
| ); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Reencoded); | ||
| expect(result.interpretation).toBe( | ||
| "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth", | ||
| ); | ||
| }); | ||
|
|
||
| it("lowercases encoded labelhash hex", () => { | ||
| const result = interpretNameFromUserInput( | ||
| "[E4310BF4547CB18B16B5348881D24A66D61FA94A013E5636B730B86EE64A3923].eth", | ||
| ); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Reencoded); | ||
| expect(result.interpretation).toBe( | ||
| "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth", | ||
| ); | ||
| }); | ||
|
|
||
| it("handles multiple encoded labelhash labels", () => { | ||
| const result = interpretNameFromUserInput( | ||
| "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].[4f5b812789fc606be1b3b16908db13fc7a9adf7ca72641f84d75b47069d3d7f0]", | ||
| ); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Reencoded); | ||
| }); | ||
| }); | ||
|
|
||
| describe("Encoded outcome", () => { | ||
| it("returns Encoded for invalid characters", () => { | ||
| const result = interpretNameFromUserInput("abc|123.eth"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Encoded); | ||
| // The unnormalizable label gets encoded as a labelhash | ||
| expect(result.interpretation).toMatch(/^\[.{64}\]\.eth$/); | ||
| }); | ||
|
|
||
| it("returns Encoded for empty labels (consecutive dots)", () => { | ||
| const result = interpretNameFromUserInput("abc..123"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Encoded); | ||
| if (result.outcome === NameInterpretationOutcomeResult.Encoded) { | ||
| expect(result.hadEmptyLabels).toBe(true); | ||
| } | ||
| }); | ||
|
|
||
| it("returns Encoded for single dot", () => { | ||
| const result = interpretNameFromUserInput("."); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Encoded); | ||
| if (result.outcome === NameInterpretationOutcomeResult.Encoded) { | ||
| expect(result.hadEmptyLabels).toBe(true); | ||
| } | ||
| }); | ||
|
|
||
| it("returns Encoded for space (non-trimmed, non-empty)", () => { | ||
| // " " trims to "" so this is Empty, but "a b" has a space inside a label | ||
| const result = interpretNameFromUserInput("a b.eth"); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Encoded); | ||
| }); | ||
|
|
||
| it("preserves inputName in Encoded result", () => { | ||
| const result = interpretNameFromUserInput("abc|123.eth"); | ||
| expect(result.inputName).toBe("abc|123.eth"); | ||
| }); | ||
|
|
||
| it("hadEmptyLabels is false when no empty labels", () => { | ||
| const result = interpretNameFromUserInput("abc|123.eth"); | ||
| if (result.outcome === NameInterpretationOutcomeResult.Encoded) { | ||
| expect(result.hadEmptyLabels).toBe(false); | ||
| } | ||
| }); | ||
| }); | ||
|
|
||
| describe("mixed label scenarios", () => { | ||
| it("normalizable + encoded labelhash = Reencoded", () => { | ||
| const result = interpretNameFromUserInput( | ||
| "VITALIK.[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923]", | ||
| ); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Reencoded); | ||
| expect(result.interpretation.startsWith("vitalik.")).toBe(true); | ||
| }); | ||
|
|
||
| it("unnormalizable takes priority over reencoded", () => { | ||
| const result = interpretNameFromUserInput( | ||
| "abc|123.[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923]", | ||
| ); | ||
| expect(result.outcome).toBe(NameInterpretationOutcomeResult.Encoded); | ||
| }); | ||
|
|
||
| it("always produces a valid interpretation string", () => { | ||
| const inputs = [ | ||
| "vitalik.eth", | ||
| "VITALIK.ETH", | ||
| "abc|123.eth", | ||
| "abc..123", | ||
| ".", | ||
| "[e4310bf4547cb18b16b5348881d24a66d61fa94a013e5636b730b86ee64a3923].eth", | ||
| ]; | ||
| for (const input of inputs) { | ||
| const result = interpretNameFromUserInput(input); | ||
| expect(typeof result.interpretation).toBe("string"); | ||
| expect(result.interpretation.length).toBeGreaterThan(0); | ||
| } | ||
| }); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original issue stated:
and most recently: