• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

cartesi / rollups-explorer / 7459483902

09 Jan 2024 09:59AM UTC coverage: 95.485%. First build
7459483902

Pull #98

github

nevendyulgerov
feat: Add coveralls badge
Pull Request #98: #97 Add build steps for generating and uploading test coverage

344 of 421 branches covered (0.0%)

Branch coverage included in aggregate %.

4858 of 5027 relevant lines covered (96.64%)

14.43 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.37
/apps/web/src/components/connectionForm.tsx
1
"use client";
1✔
2
import {
1✔
3
    Alert,
1✔
4
    Autocomplete,
1✔
5
    Button,
1✔
6
    Flex,
1✔
7
    List,
1✔
8
    Loader,
1✔
9
    Text,
1✔
10
    TextInput,
1✔
11
    useMantineTheme,
1✔
12
} from "@mantine/core";
1✔
13
import { useForm } from "@mantine/form";
1✔
14
import { useDebouncedValue } from "@mantine/hooks";
1✔
15
import { notifications } from "@mantine/notifications";
1✔
16
import { isEmpty } from "ramda";
1✔
17
import React, { FC, useState } from "react";
1✔
18
import {
1✔
19
    TbAlertCircle,
1✔
20
    TbCheck,
1✔
21
    TbPlugConnected,
1✔
22
    TbPlugConnectedX,
1✔
23
} from "react-icons/tb";
1✔
24
import { UseQueryExecute, UseQueryState, useQuery } from "urql";
1✔
25
import { Address, isAddress } from "viem";
1✔
26
import {
1✔
27
    ApplicationsDocument,
1✔
28
    ApplicationsQuery,
1✔
29
    ApplicationsQueryVariables,
1✔
30
} from "../graphql";
1✔
31
import {
1✔
32
    CheckStatusDocument,
1✔
33
    CheckStatusQuery,
1✔
34
    CheckStatusQueryVariables,
1✔
35
} from "../graphql/rollups/operations";
1✔
36
import { useConnectionConfig } from "../providers/connectionConfig/hooks";
1✔
37

1✔
38
interface AppConnectionFormProps {
1✔
39
    application?: Address;
1✔
40
    onSubmitted?: () => void;
1✔
41
}
1✔
42

1✔
43
type UseSearchApplications = (params: {
1✔
44
    address: Address;
1✔
45
    limit?: number;
1✔
46
}) => [{ applications: string[]; fetching: boolean }, UseQueryExecute];
1✔
47

1✔
48
interface DisplayQueryResultProps {
1✔
49
    result: UseQueryState<CheckStatusQuery, CheckStatusQueryVariables>;
1✔
50
}
1✔
51

1✔
52
const DisplayQueryResult: FC<DisplayQueryResultProps> = ({ result }) => {
1✔
53
    return (
17✔
54
        <>
17✔
55
            {result?.data && (
17✔
56
                <Alert
2✔
57
                    title="This application responded with"
2✔
58
                    icon={<TbCheck />}
2✔
59
                    variant="light"
2✔
60
                    color="green"
2✔
61
                >
2✔
62
                    <List>
2✔
63
                        <List.Item>
2✔
64
                            {result.data.inputs.totalCount} Inputs
2✔
65
                        </List.Item>
2✔
66
                        <List.Item>
2✔
67
                            {result.data.notices.totalCount} Notices
2✔
68
                        </List.Item>
2✔
69
                        <List.Item>
2✔
70
                            {result.data.vouchers.totalCount} Vouchers
2✔
71
                        </List.Item>
2✔
72
                        <List.Item>
2✔
73
                            {result.data.reports.totalCount} Reports
2✔
74
                        </List.Item>
2✔
75
                    </List>
2✔
76
                </Alert>
2✔
77
            )}
17✔
78

17✔
79
            {result?.error && (
17✔
80
                <Alert
4✔
81
                    variant="light"
4✔
82
                    icon={<TbAlertCircle />}
4✔
83
                    title="Something went wrong"
4✔
84
                    color="red"
4✔
85
                >
4✔
86
                    <Text>{result.error.message}</Text>
4✔
87
                </Alert>
4✔
88
            )}
17✔
89
        </>
17✔
90
    );
17✔
91
};
17✔
92

1✔
93
const checkURL = (url: string) => {
1✔
94
    try {
13✔
95
        const result = new URL(url);
13✔
96
        return {
13✔
97
            validURL: true,
13✔
98
            result,
13✔
99
            url,
13✔
100
        };
13✔
101
    } catch (error: any) {
13✔
102
        return {
11✔
103
            validURL: false,
11✔
104
            error: error as TypeError,
11✔
105
            url,
11✔
106
        };
11✔
107
    }
11✔
108
};
13✔
109

1✔
110
const useSearchApplications: UseSearchApplications = ({
1✔
111
    address,
30✔
112
    limit,
30✔
113
}): [{ applications: string[]; fetching: boolean }, UseQueryExecute] => {
30✔
114
    const [result, executeQuery] = useQuery<
30✔
115
        ApplicationsQuery,
30✔
116
        ApplicationsQueryVariables
30✔
117
    >({
30✔
118
        query: ApplicationsDocument,
30✔
119
        variables: {
30✔
120
            limit: limit ?? 10,
30✔
121
            where: {
30✔
122
                id_containsInsensitive: address ?? "",
30!
123
            },
30✔
124
        },
30✔
125
    });
30✔
126
    const data = result.data;
30✔
127
    const applications = React.useMemo(
30✔
128
        () => (data?.applications ?? []).map((a) => a.id),
30!
129
        [data],
30✔
130
    );
30✔
131

30✔
132
    return [{ applications, fetching: result.fetching }, executeQuery];
30✔
133
};
30✔
134

1✔
135
const AppConnectionForm: FC<AppConnectionFormProps> = ({
1✔
136
    application,
30✔
137
    onSubmitted,
30✔
138
}) => {
30✔
139
    const { addConnection, hasConnection } = useConnectionConfig();
30✔
140
    const theme = useMantineTheme();
30✔
141
    const form = useForm({
30✔
142
        validateInputOnChange: true,
30✔
143
        initialValues: {
30✔
144
            address: application ?? "",
30✔
145
            url: "",
30✔
146
        },
30✔
147
        validate: {
30✔
148
            address: (v) => {
30✔
149
                if (isEmpty(v)) return `Address is a required field!`;
10✔
150

6✔
151
                if (!isAddress(v)) {
10✔
152
                    return `It is not a valid address format.`;
1✔
153
                }
1✔
154

5✔
155
                if (hasConnection(v)) {
10✔
156
                    return `There is an connection for that address`;
1✔
157
                }
1✔
158

4✔
159
                return null;
4✔
160
            },
10✔
161
            url: (v) => {
30✔
162
                if (isEmpty(v)) return "URL is a required field!";
10✔
163
            },
10✔
164
        },
30✔
165
        transformValues: (values) => ({
30✔
166
            address: values.address as Address,
32✔
167
            url: values.url,
32✔
168
        }),
32✔
169
    });
30✔
170
    const [submitting, setSubmitting] = useState(false);
30✔
171

30✔
172
    const { url, address } = form.getTransformedValues();
30✔
173
    const [debouncedAddress] = useDebouncedValue(address, 400);
30✔
174
    const [debouncedUrl] = useDebouncedValue(url, 300);
30✔
175

30✔
176
    const [{ applications, fetching }] = useSearchApplications({
30✔
177
        address: debouncedAddress,
30✔
178
    });
30✔
179

30✔
180
    const showLoader = !isEmpty(debouncedAddress) && fetching;
30✔
181

30✔
182
    const { validURL } = React.useMemo(
30✔
183
        () => checkURL(debouncedUrl),
30✔
184
        [debouncedUrl],
30✔
185
    );
30✔
186

30✔
187
    const [result] = useQuery<CheckStatusQuery, CheckStatusQueryVariables>({
30✔
188
        query: CheckStatusDocument,
30✔
189
        pause: !validURL,
30✔
190
        context: React.useMemo(
30✔
191
            () => ({
30✔
192
                url: debouncedUrl,
13✔
193
                requestPolicy: "network-only",
13✔
194
            }),
13✔
195
            [debouncedUrl],
30✔
196
        ),
30✔
197
    });
30✔
198

30✔
199
    const { operation } = result;
30✔
200
    const displayQueryResult = url === operation?.context?.url;
30✔
201
    const testSuccess = !result.fetching && !result.stale && !result.error;
30✔
202

30✔
203
    const onSuccess = () => {
30✔
204
        const { address } = form.getTransformedValues();
×
205
        notifications.show({
×
206
            message: `Connection ${address} created with success`,
×
207
            color: "green",
×
208
            withBorder: true,
×
209
        });
×
210
        form.reset();
×
211
        onSubmitted && onSubmitted();
×
212
    };
×
213

30✔
214
    const onFailure = () => {
30✔
215
        notifications.show({
×
216
            message: `Failed to create connection.`,
×
217
            color: "red",
×
218
            withBorder: true,
×
219
        });
×
220
    };
×
221

30✔
222
    const onFinished = () => {
30✔
223
        setSubmitting((v) => !v);
×
224
    };
×
225

30✔
226
    return (
30✔
227
        <form
30✔
228
            onSubmit={form.onSubmit((values) => {
30✔
229
                if (testSuccess) {
2✔
230
                    setSubmitting(true);
1✔
231
                    addConnection(values, {
1✔
232
                        onFinished,
1✔
233
                        onSuccess,
1✔
234
                        onFailure,
1✔
235
                    });
1✔
236
                } else {
1✔
237
                    notifications.show({
1✔
238
                        message:
1✔
239
                            "To save a connection the endpoint needs to be working",
1✔
240
                        color: "orange",
1✔
241
                        withBorder: true,
1✔
242
                    });
1✔
243
                }
1✔
244
            })}
30✔
245
        >
30✔
246
            <Flex direction="column" gap="sm">
30✔
247
                <Autocomplete
30✔
248
                    withAsterisk
30✔
249
                    label="Address"
30✔
250
                    description="The application smart contract address."
30✔
251
                    rightSection={showLoader ? <Loader size="sm" /> : ""}
30!
252
                    placeholder="0x"
30✔
253
                    data={applications}
30✔
254
                    {...form.getInputProps("address")}
30✔
255
                />
30✔
256

30✔
257
                {isAddress(address) && !applications.length && !fetching && (
30✔
258
                    <Alert
1✔
259
                        variant="light"
1✔
260
                        color="yellow"
1✔
261
                        icon={<TbAlertCircle />}
1✔
262
                    >
1✔
263
                        <Text>
1✔
264
                            This is the address of an undeployed application.
1✔
265
                        </Text>
1✔
266
                    </Alert>
1✔
267
                )}
30✔
268

30✔
269
                <TextInput
30✔
270
                    label="URL"
30✔
271
                    withAsterisk
30✔
272
                    placeholder="https://app-hostname/graphql"
30✔
273
                    description="The rollups graphQL endpoint"
30✔
274
                    rightSectionPointerEvents="none"
30✔
275
                    rightSection={
30✔
276
                        result.fetching || result.stale ? (
30✔
277
                            <Loader data-testid="icon-test-loading" size="sm" />
3✔
278
                        ) : !validURL || !url ? (
27✔
279
                            <TbPlugConnected
25✔
280
                                data-testid="icon-test-inactive"
25✔
281
                                size={theme.other.iconSize}
25✔
282
                                color={theme.colors.gray[5]}
25✔
283
                            />
25✔
284
                        ) : validURL && !testSuccess ? (
2✔
285
                            <TbPlugConnectedX
1✔
286
                                data-testid="icon-test-failed"
1✔
287
                                size={theme.other.iconSize}
1✔
288
                                color="red"
1✔
289
                            />
1✔
290
                        ) : (
1✔
291
                            <TbPlugConnected
1✔
292
                                data-testid="icon-test-success"
1✔
293
                                size={theme.other.iconSize}
1✔
294
                                color={theme.primaryColor}
1✔
295
                            />
1✔
296
                        )
30✔
297
                    }
30✔
298
                    {...form.getInputProps("url")}
30✔
299
                />
30✔
300

30✔
301
                {displayQueryResult && <DisplayQueryResult result={result} />}
30✔
302
            </Flex>
30✔
303

30✔
304
            <Flex direction="row" justify="flex-end" align="center" pt="xl">
30✔
305
                <Button type="submit" loading={submitting}>
30✔
306
                    Save
30✔
307
                </Button>
30✔
308
            </Flex>
30✔
309
        </form>
30✔
310
    );
30✔
311
};
30✔
312

1✔
313
export default AppConnectionForm;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc