Fix 404s in timeline and legacy-mode interlinks (#1305)

Summary:
All links in SourceCred must use the `Link` component, providing either
an external URL `href={…}` or an internal route `to={…}`. Any uses of a
raw `<a>` element for internal routes will incur 404s when the
application is hosted on a non-root path, as is currently the case on
the production website.

The change to `FileUploader` is not strictly necessary, as the link has
no styled text and uses a `data:` URL, but there’s no reason not to.

Fixes #1304.

Test Plan:
Build the static site:

```
scripts/build_static_site.sh --target cred --project sourcecred/example-github
```

Then run `python3 -m http.server` from the repository root directory—not
the `cred/` subdirectory—and navigate to the timeline cred view:
<http://localhost:8000/cred/timeline/sourcecred/example-github/>

Observe that the “(legacy)” link now has the correct styling and
correctly navigates to the legacy mode page when clicked: prior to this
change, it would navigate to a URL without the proper `/cred/` path
prefix, yielding a 404. On the legacy page, verify that the “timeline
mode” link has the same properties.

Then, visit <http://localhost:8000/cred/test/FileUploader/> and verify
that the inspection test still passes.

Added a regression test to catch further such errors. Note that
reverting the code changes in this commit causes the test to fail, and
that running it with `--verbose` prints the problematic files.

wchargin-branch: fix-bad-routing-404s
This commit is contained in:
William Chargin 2019-08-18 14:43:34 -07:00 committed by GitHub
parent 0eeda22e0c
commit acdcbc29ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 38 additions and 4 deletions

View File

@ -0,0 +1,32 @@
#!/bin/sh
# shellcheck disable=SC2016
#
# Components must use the `Link` component from `webutil/Link.js` rather
# than raw `<a>` elements. The `Link` component properly handles
# client-side routing, as well as providing consistent styles.
#
# See <https://github.com/sourcecred/sourcecred/pull/1305> for an
# example of how to fix this error.
export GIT_CONFIG_NOSYSTEM=1
export GIT_ATTR_NOSYSTEM=1
# shellcheck disable=SC2034
test_description='check that bare <a> elements are never directly used'
# shellcheck disable=SC1091
. ./sharness.sh
# shellcheck disable=SC1004
test_expect_success "application components must use <Link> instead of <a>" '
test_must_fail git grep -nF "</a>" \
":/src/*.js" \
":(exclude,top)*/__snapshots__/*" \
":(exclude,top)*/snapshots/*" \
":(exclude,top)src/webutil/Link.js" \
;
'
test_done
# vim: ft=sh

View File

@ -10,6 +10,7 @@ import {
type TimelineCredParameters, type TimelineCredParameters,
} from "../analysis/timeline/timelineCred"; } from "../analysis/timeline/timelineCred";
import {TimelineCredView} from "./TimelineCredView"; import {TimelineCredView} from "./TimelineCredView";
import Link from "../webutil/Link";
import {WeightConfig} from "./weights/WeightConfig"; import {WeightConfig} from "./weights/WeightConfig";
import {WeightsFileManager} from "./weights/WeightsFileManager"; import {WeightsFileManager} from "./weights/WeightsFileManager";
import {type NodeType} from "../analysis/types"; import {type NodeType} from "../analysis/types";
@ -121,7 +122,7 @@ export class TimelineExplorer extends React.Component<Props, State> {
<div style={{marginTop: 30, display: "flex"}}> <div style={{marginTop: 30, display: "flex"}}>
<span style={{paddingLeft: 30}}> <span style={{paddingLeft: 30}}>
cred for {this.props.projectId} cred for {this.props.projectId}
<a href={`/prototype/${this.props.projectId}/`}>(legacy)</a> <Link to={`/prototype/${this.props.projectId}/`}>(legacy)</Link>
</span> </span>
<span style={{flexGrow: 1}} /> <span style={{flexGrow: 1}} />
{this.renderFilterSelect()} {this.renderFilterSelect()}

View File

@ -146,7 +146,7 @@ export function createApp(
<h2>SourceCred Legacy Mode</h2> <h2>SourceCred Legacy Mode</h2>
<p> <p>
Back to{" "} Back to{" "}
<a href={`/timeline/${this.props.projectId}/`}>timeline mode</a> <Link to={`/timeline/${this.props.projectId}/`}>timeline mode</Link>
</p> </p>
<ProjectDetail projectId={this.props.projectId} /> <ProjectDetail projectId={this.props.projectId} />

View File

@ -3,6 +3,7 @@
import stringify from "json-stable-stringify"; import stringify from "json-stable-stringify";
import React from "react"; import React from "react";
import {FileUploader} from "../../util/FileUploader"; import {FileUploader} from "../../util/FileUploader";
import Link from "../../webutil/Link";
import {MdFileDownload, MdFileUpload} from "react-icons/md"; import {MdFileDownload, MdFileUpload} from "react-icons/md";
import {type Weights, toJSON, fromJSON} from "../../analysis/weights"; import {type Weights, toJSON, fromJSON} from "../../analysis/weights";
@ -16,14 +17,14 @@ export class WeightsFileManager extends React.Component<Props> {
const onUpload = (json) => this.props.onWeightsChange(fromJSON(json)); const onUpload = (json) => this.props.onWeightsChange(fromJSON(json));
return ( return (
<div> <div>
<a <Link
download="weights.json" download="weights.json"
title="Download your weights.json" title="Download your weights.json"
href={`data:text/json,${weightsJSON}`} href={`data:text/json,${weightsJSON}`}
style={{color: "black"}} style={{color: "black"}}
> >
<MdFileDownload style={{margin: "2px"}} /> <MdFileDownload style={{margin: "2px"}} />
</a> </Link>
<FileUploader title="Upload weights.json" onUpload={onUpload}> <FileUploader title="Upload weights.json" onUpload={onUpload}>
<MdFileUpload style={{margin: "2px"}} /> <MdFileUpload style={{margin: "2px"}} />
</FileUploader> </FileUploader>