Skip to content

Commit

Permalink
Merge pull request #1 from EyeSeeTea/release/0.1.0
Browse files Browse the repository at this point in the history
[Merge] Release 0.1.0
  • Loading branch information
ifoche authored Oct 1, 2021
2 parents 5a095e3 + 1b913d3 commit ead3cea
Show file tree
Hide file tree
Showing 95 changed files with 63,139 additions and 1,784 deletions.
6 changes: 5 additions & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
BROWSER=false
PORT=8081
SKIP_PREFLIGHT_CHECK=true
REACT_APP_DHIS2_BASE_URL=http://dev2.eyeseetea.com:8085/
REACT_APP_DHIS2_BASE_URL=https://dev.eyeseetea.com/play

CYPRESS_DHIS2_AUTH='admin:district'
CYPRESS_EXTERNAL_API="https://dev.eyeseetea.com/play"
CYPRESS_ROOT_URL=http://localhost:8081
4 changes: 3 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module.exports = {
"no-console": ["warn", { allow: ["debug", "warn", "error"] }],
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/explicit-function-return-type": ["off"],
"unused-imports/no-unused-imports": "warn",
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
"react/prop-types": "off",
"react/display-name": "off",
Expand All @@ -21,6 +22,7 @@ module.exports = {
"no-useless-concat": "off",
"no-useless-constructor": "off",
"no-unexpected-multiline": "off",
"no-loop-func": "off",
"default-case": "off",
"@typescript-eslint/no-use-before-define": "off",
"@typescript-eslint/no-explicit-any": "off",
Expand All @@ -42,7 +44,7 @@ module.exports = {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
plugins: ["cypress", "@typescript-eslint", "react-hooks"],
plugins: ["cypress", "@typescript-eslint", "react-hooks", "unused-imports"],
env: { "cypress/globals": true },
settings: {
react: {
Expand Down
34 changes: 16 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,37 @@ $ yarn install

## Development

Start development server:
Start the development server:

```
$ PORT=8081 REACT_APP_DHIS2_BASE_URL="http://localhost:8080" yarn start
```

Linting:
Now in your browser, go to `http://localhost:8081`.

```
$ yarn lint
```
Notes:

- Requests to DHIS2 will be transparently proxied (see `src/setupProxy.js`) from `http://localhost:8081/dhis2/path` to `http://localhost:8080/path` to avoid CORS and cross-domain problems.

- The optional environment variable `REACT_APP_DHIS2_AUTH=USERNAME:PASSWORD` forces some credentials to be used by the proxy. This variable is usually not set, so the app has the same user logged in at `REACT_APP_DHIS2_BASE_URL`.

- Create a file `.env.local` (copy it from `.env`) to customize environment variables so you can simply run `yarn start`.

- [why-did-you-render](https://github.com/welldone-software/why-did-you-render) is installed, but it does not work when using standard react scripts (`yarn start`). Instead, use `yarn craco-start` to debug re-renders with WDYR. Note that hot reloading does not work out-of-the-box with [craco](https://github.com/gsoft-inc/craco).

## Tests

Run unit tests:
### Unit tests

```
$ yarn test
```

Run integration tests locally:
### Integration tests (Cypress)

Create the required users for testing (`cypress/support/App.ts`) in your instance and run:

```
$ export CYPRESS_DHIS2_AUTH='admin:district'
$ export CYPRESS_EXTERNAL_API="http://localhost:8080"
$ export CYPRESS_ROOT_URL=http://localhost:8081
Expand All @@ -42,13 +49,10 @@ $ yarn cy:e2e:run
$ yarn cy:e2e:open
```

For this to work in Travis, you will have to create an environment variable `CYPRESS_DHIS2_AUTH`
(Settings -> Environment Variables) with the `user:password` used in your testing DHIS2 instance.

## Build app ZIP

```
$ yarn build-webapp
$ yarn build
```

## Some development tips
Expand All @@ -69,15 +73,9 @@ $ yarn build-webapp
### i18n

```
$ yarn update-po
# ... add/edit translations in i18n/*.po files ...
$ yarn localize
```

### App context

The file `src/contexts/app-context.ts` holds some general context so typical infrastructure objects (`api`, `d2`, ...) are readily available. Add your own global objects if necessary.

### Scripts

Check the example script, entry `"script-example"`in `package.json`->scripts and `src/scripts/example.ts`.
8 changes: 3 additions & 5 deletions babel.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
module.exports = process.env.CYPRESS_E2E
? {}
: {
presets: ["@babel/typescript", ["babel-preset-react-app", { runtime: "automatic" }]],
};
module.exports = {
presets: ["@babel/typescript", ["babel-preset-react-app", { runtime: "automatic" }]],
};
30 changes: 30 additions & 0 deletions craco.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* Required for CRA 4, see https://github.com/welldone-software/why-did-you-render/issues/154#issuecomment-773905769 */

module.exports = {
babel: {
loaderOptions: babelLoaderOptions => {
const origBabelPresetCRAIndex = babelLoaderOptions.presets.findIndex(preset => {
return preset[0].includes("babel-preset-react-app");
});

const origBabelPresetCRA = babelLoaderOptions.presets[origBabelPresetCRAIndex];

babelLoaderOptions.presets[origBabelPresetCRAIndex] = function overridenPresetCRA(api, opts, env) {
const babelPresetCRAResult = require(origBabelPresetCRA[0])(api, origBabelPresetCRA[1], env);

babelPresetCRAResult.presets.forEach(preset => {
// detect @babel/preset-react with {development: true, runtime: 'automatic'}
const isReactPreset =
preset && preset[1] && preset[1].runtime === "automatic" && preset[1].development === true;
if (isReactPreset) {
preset[1].importSource = "@welldone-software/why-did-you-render";
}
});

return babelPresetCRAResult;
};

return babelLoaderOptions;
},
},
};
12 changes: 1 addition & 11 deletions cypress/integration/example-page.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@
context("Example page", () => {
before(() => {
cy.login("admin");
cy.visit("#/for");
});

it("increments counter when button clicked", () => {
cy.contains("+1").click();
cy.contains("Value=1");
});

it("shows feedback when button clicked", () => {
cy.contains("Click to show feedback").click();
cy.contains("Some info");
cy.visit("/");
});
});
12 changes: 0 additions & 12 deletions cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,8 @@
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config

on("task", {
logRequest(req) {
console.debug(req, config);
Expand Down
57 changes: 57 additions & 0 deletions cypress/support/App.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { getApiUrl } from "./commands";

const users = {
admin: { username: "cypress-admin", password: "Testing123$" },
basic: { username: "cypress-basic", password: "Testing123$" },
};

export class App {
/* Constructors */

static get(options: { user: UserKey }): App {
const app = new App();
app.load(users[options.user]);
return app;
}

/* Generic Helpers */

load(auth: { username: string; password: string }) {
cy.request({ method: "GET", url: getApiUrl("/api/me"), auth, log: true }).then(res => {
expect(res.status).to.equal(200);
});

cy.visit("/");
}

shouldContainText(text: string, options?: { clickable: boolean }) {
if (options?.clickable) {
cy.contains(text).should("have.css", "cursor", "pointer");
} else {
cy.contains(text);
}
}

shouldNotContainText(text: string) {
cy.findByText(text).should("not.exist");
}

shouldNotShowText(text: string) {
cy.findByText(text).should("not.be.visible");
}

clickOnText(text: string) {
cy.contains(text).click();
}

/* MUI helpers */

selectOptionSelector(options: { label: string; select: string }) {
const { label, select } = options;
this.shouldContainText(label);
this.clickOnText(label);
cy.findByRole("presentation").findByText(select).click();
}
}

type UserKey = keyof typeof users;
96 changes: 0 additions & 96 deletions cypress/support/commands.js

This file was deleted.

28 changes: 28 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import "@testing-library/cypress/add-commands";
import * as qs from "qs";

const appUrl: string = Cypress.env("ROOT_URL") || "";

const dhis2Url: string = appUrl + "/dhis2";

function setup() {
Cypress.config("baseUrl", appUrl);

Cypress.Cookies.defaults({ preserve: "JSESSIONID" });

Cypress.on("uncaught:exception", (err, runnable) => {
console.error("uncaught:exception", { err, runnable });
// returning false here prevents Cypress from failing the test
return false;
});
}

setup();

/* Public interface */

export function getApiUrl(path: string, params?: Record<string, number | boolean | string | string[]>) {
const baseUrl = dhis2Url.replace(/\/$/, "") + "/" + path.replace(/^\//, "");
const queryString = qs.stringify(params || {}, { arrayFormat: "repeat", addQueryPrefix: true });
return baseUrl + queryString;
}
16 changes: 0 additions & 16 deletions cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,2 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import "./commands";
import "cypress-xpath";
Loading

0 comments on commit ead3cea

Please sign in to comment.