Skip to content

Commit

Permalink
Feat: editorial workflow bitbucket gitlab (decaporg#3014)
Browse files Browse the repository at this point in the history
* refactor: typescript the backends

* feat: support multiple files upload for GitLab and BitBucket

* fix: load entry media files from media folder or UI state

* chore: cleanup log message

* chore: code cleanup

* refactor: typescript the test backend

* refactor: cleanup getEntry unsued variables

* refactor: moved shared backend code to lib util

* chore: rename files to preserve history

* fix: bind readFile method to API classes

* test(e2e): switch to chrome in cypress tests

* refactor: extract common api methods

* refactor: remove most of immutable js usage from backends

* feat(backend-gitlab): initial editorial workflow support

* feat(backend-gitlab): implement missing workflow methods

* chore: fix lint error

* feat(backend-gitlab): support files deletion

* test(e2e): add gitlab cypress tests

* feat(backend-bitbucket): implement missing editorial workflow methods

* test(e2e): add BitBucket backend e2e tests

* build: update node version to 12 on netlify builds

* fix(backend-bitbucket): extract BitBucket avatar url

* test: fix git-gateway AuthenticationPage test

* test(e2e): fix some backend tests

* test(e2e): fix tests

* test(e2e): add git-gateway editorial workflow test

* chore: code cleanup

* test(e2e): revert back to electron

* test(e2e): add non editorial workflow tests

* fix(git-gateway-gitlab): don't call unpublishedEntry in simple workflow

gitlab git-gateway doesn't support editorial workflow APIs yet. This change makes sure not to call them in simple workflow

* refactor(backend-bitbucket): switch to diffstat API instead of raw diff

* chore: fix test

* test(e2e): add more git-gateway tests

* fix: post rebase typescript fixes

* test(e2e): fix tests

* fix: fix parsing of content key and add tests

* refactor: rename test file

* test(unit): add getStatues unit tests

* chore: update cypress

* docs: update beta docs
  • Loading branch information
erezrokah authored and erquhart committed Jan 14, 2020
1 parent 4ff5bc2 commit 6f221ab
Show file tree
Hide file tree
Showing 251 changed files with 67,279 additions and 12,343 deletions.
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ module.exports = {
},
rules: {
'require-atomic-updates': [0],
'import/no-unresolved': [0],
'@typescript-eslint/no-non-null-assertion': [0],
'@typescript-eslint/explicit-function-return-type': 0,
'@typescript-eslint/no-use-before-define': [
'error',
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
8
12
28 changes: 19 additions & 9 deletions cypress/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ During the setup of a spec file, the relevant `index.html` and `config.yml` are

Tests for the `test` backend use mock data generated in `dev-test/backends/test/index.html`.

Tests for the `github` backend use previously [recorded data](fixtures) and stub `fetch` [calls](support/commands.js#L82). See more about recording tests data [here](#recording-tests-data).
Tests for the other backends use previously [recorded data](fixtures) and stub `fetch` [calls](support/commands.js#L52). See more about recording tests data [here](#recording-tests-data).

## Run Tests Locally

Expand All @@ -27,19 +27,29 @@ yarn test:e2e:exec # runs Cypress in non-headless mode with mock data

## Recording Tests Data

> Currently only relevant for `github` backend tests.
When recording tests, access to the GitHub API is required, thus one must set up a `.env` file in the root project directory in the following format:
When recording tests, access to the relevant backend API is required, thus one must set up a `.env` file in the root project directory in the following format:

```bash
GITHUB_REPO_OWNER=owner
GITHUB_REPO_NAME=repo
GITHUB_REPO_TOKEN=tokenWithWritePermissions
GITHUB_OPEN_AUTHORING_OWNER=forkOwner
GITHUB_OPEN_AUTHORING_TOKEN=tokenWithWritePermissions

GITLAB_REPO_OWNER=owner
GITLAB_REPO_NAME=repo
GITLAB_REPO_TOKEN=tokenWithWritePermissions

BITBUCKET_REPO_OWNER=owner
BITBUCKET_REPO_NAME=repo
BITBUCKET_OUATH_CONSUMER_KEY=ouathConsumerKey
BITBUCKET_OUATH_CONSUMER_SECRET=ouathConsumerSecret

NETLIFY_API_TOKEN=netlifyApiToken
NETLIFY_INSTALLATION_ID=netlifyGitHubInstallationId
```

> The structure of the repo designated by `GITHUB_REPO_OWNER/GITHUB_REPO_NAME` should match the settings in [`config.yml`](../dev-test/backends/github/config.yml#L1)
> The structure of the relevant repo should match the settings in [`config.yml`](../dev-test/backends/<backend>/config.yml#L1)
To start a recording run the following commands:

Expand All @@ -50,9 +60,9 @@ yarn test:e2e:record-fixtures:dev # runs Cypress in non-headless and pass data t
yarn mock:server:stop # stops the recording proxy
```

> During the recorded process a clone of `GITHUB_REPO_NAME` will be created under `.temp` and reset between tests.
> During the recorded process a clone of the relevant repo will be created under `.temp` and reset between tests.
Recordings are sanitized from any possible sensitive data and [transformed](plugins/github.js#L395) into an easier to process format.
Recordings are sanitized from any possible sensitive data and [transformed](plugins/common.js#L34) into an easier to process format.

To avoid recording all the tests over and over again, a recommended process is to:

Expand All @@ -65,8 +75,8 @@ To avoid recording all the tests over and over again, a recommended process is t

Most common failures are:

1. The [recorded data](utils/mock-server.js#L17) is not [transformed](plugins/github.js#L395) properly (e.g. [sanitization](plugins/github.js#L283) broke something).
2. The [stubbed requests and responses](support/commands.js#L82) are not [matched](support/commands.js#L29) properly (e.g. timestamp changes in request body between recording and playback).
1. The [recorded data](utils/mock-server.js#L17) is not [transformed](plugins/common.js#L34) properly (e.g. sanitization broke something).
2. The [stubbed requests and responses](support/commands.js#L82) are not [matched](support/commands.js#L32) properly (e.g. timestamp changes in request body between recording and playback).

Dumping all recorded data as is to a file [here](utils/mock-server.js#L24) and adding a `debugger;` statement [here](support/commands.js#L52) is useful to gain insights.

Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1,699 changes: 1,699 additions & 0 deletions cypress/fixtures/BitBucket Backend Editorial Workflow__can update an entry.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

1,412 changes: 1,412 additions & 0 deletions cypress/fixtures/GitLab Backend Editorial Workflow__can update an entry.json

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

93 changes: 93 additions & 0 deletions cypress/integration/common/editorial_workflow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import '../../utils/dismiss-local-backup';
import {
login,
createPost,
createPostAndExit,
updateExistingPostAndExit,
exitEditor,
goToWorkflow,
goToCollections,
updateWorkflowStatus,
publishWorkflowEntry,
assertWorkflowStatusInEditor,
assertPublishedEntry,
deleteEntryInEditor,
assertOnCollectionsPage,
assertEntryDeleted,
assertWorkflowStatus,
updateWorkflowStatusInEditor,
} from '../../utils/steps';
import { workflowStatus, editorStatus } from '../../utils/constants';

export default function({ entries, getUser }) {
it('successfully loads', () => {
login(getUser());
});

it('can create an entry', () => {
login(getUser());
createPostAndExit(entries[0]);
});

it('can update an entry', () => {
login(getUser());
createPostAndExit(entries[0]);
updateExistingPostAndExit(entries[0], entries[1]);
});

it('can publish an editorial workflow entry', () => {
login(getUser());
createPostAndExit(entries[0]);
goToWorkflow();
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
publishWorkflowEntry(entries[0]);
});

it('can change workflow status', () => {
login(getUser());
createPostAndExit(entries[0]);
goToWorkflow();
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);
updateWorkflowStatus(entries[0], workflowStatus.review, workflowStatus.ready);
updateWorkflowStatus(entries[0], workflowStatus.ready, workflowStatus.review);
updateWorkflowStatus(entries[0], workflowStatus.review, workflowStatus.draft);
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
});

it('can change status on and publish multiple entries', () => {
login(getUser());
createPostAndExit(entries[0]);
createPostAndExit(entries[1]);
createPostAndExit(entries[2]);
goToWorkflow();
updateWorkflowStatus(entries[2], workflowStatus.draft, workflowStatus.ready);
updateWorkflowStatus(entries[1], workflowStatus.draft, workflowStatus.ready);
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
publishWorkflowEntry(entries[2]);
publishWorkflowEntry(entries[1]);
publishWorkflowEntry(entries[0]);
goToCollections();
assertPublishedEntry([entries[2], entries[1], entries[0]]);
});

it('can delete an entry', () => {
login(getUser());
createPost(entries[0]);
deleteEntryInEditor();
assertOnCollectionsPage();
assertEntryDeleted(entries[0]);
});

it('can update workflow status from within the editor', () => {
login(getUser());
createPost(entries[0]);
assertWorkflowStatusInEditor(editorStatus.draft);
updateWorkflowStatusInEditor(editorStatus.review);
assertWorkflowStatusInEditor(editorStatus.review);
updateWorkflowStatusInEditor(editorStatus.ready);
assertWorkflowStatusInEditor(editorStatus.ready);
exitEditor();
goToWorkflow();
assertWorkflowStatus(entries[0], workflowStatus.ready);
});
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ function assertImagesInLibrary() {
}

function assertNoImagesInLibrary() {
cy.get('h1')
.contains('Loading...')
.should('not.exist');
cy.get('img[class*="CardImage"]').should('not.exist');
}

Expand Down
76 changes: 76 additions & 0 deletions cypress/integration/common/open_authoring.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import '../../utils/dismiss-local-backup';
import {
login,
createPostAndExit,
updateExistingPostAndExit,
goToWorkflow,
deleteWorkflowEntry,
updateWorkflowStatus,
publishWorkflowEntry,
} from '../../utils/steps';
import { workflowStatus } from '../../utils/constants';

export default function({ entries, getUser, getForkUser }) {
it('successfully loads', () => {
login(getUser());
});

it('can create an entry', () => {
login(getUser());
createPostAndExit(entries[0]);
});

it('can update an entry', () => {
login(getUser());
createPostAndExit(entries[0]);
updateExistingPostAndExit(entries[0], entries[1]);
});

it('can publish an editorial workflow entry', () => {
login(getUser());
createPostAndExit(entries[0]);
goToWorkflow();
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.ready);
publishWorkflowEntry(entries[0]);
});

it('successfully forks repository and loads', () => {
login(getForkUser());
});

it('can create an entry on fork', () => {
login(getForkUser());
createPostAndExit(entries[0]);
});

it('can update a draft entry on fork', () => {
login(getForkUser());
createPostAndExit(entries[0]);
updateExistingPostAndExit(entries[0], entries[1]);
});

it('can change entry status from fork', () => {
login(getForkUser());
createPostAndExit(entries[0]);
goToWorkflow();
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);
});

it('can delete review entry from fork', () => {
login(getForkUser());
createPostAndExit(entries[0]);
goToWorkflow();
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);
deleteWorkflowEntry(entries[0]);
});

it('can return entry to draft and delete it', () => {
login(getForkUser());
createPostAndExit(entries[0]);
goToWorkflow();
updateWorkflowStatus(entries[0], workflowStatus.draft, workflowStatus.review);

updateWorkflowStatus(entries[0], workflowStatus.review, workflowStatus.draft);
deleteWorkflowEntry(entries[0]);
});
}
14 changes: 14 additions & 0 deletions cypress/integration/common/simple_workflow.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import '../../utils/dismiss-local-backup';
import { login, createPostAndPublish, assertPublishedEntry } from '../../utils/steps';

export default function({ entries, getUser }) {
it('successfully loads', () => {
login(getUser());
});

it('can create an entry', () => {
login(getUser());
createPostAndPublish(entries[0]);
assertPublishedEntry(entries[0]);
});
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
export const before = (taskResult, options, backend = 'github') => {
export const before = (taskResult, options, backend) => {
Cypress.config('taskTimeout', 7 * 60 * 1000);
cy.task('setupBackend', { backend, options }).then(data => {
taskResult.data = data;
Cypress.config('defaultCommandTimeout', data.mockResponses ? 5 * 1000 : 1 * 60 * 1000);
});
};

export const after = (taskResult, backend = 'github') => {
export const after = (taskResult, backend) => {
cy.task('teardownBackend', {
backend,
...taskResult.data,
});
};

export const beforeEach = (taskResult, backend = 'github') => {
export const beforeEach = (taskResult, backend) => {
const spec = Cypress.mocha.getRunner().suite.ctx.currentTest.parent.title;
const testName = Cypress.mocha.getRunner().suite.ctx.currentTest.title;
cy.task('setupBackendTest', {
Expand All @@ -32,7 +32,7 @@ export const beforeEach = (taskResult, backend = 'github') => {
return cy.clock(0, ['Date']);
};

export const afterEach = (taskResult, backend = 'github') => {
export const afterEach = (taskResult, backend) => {
const spec = Cypress.mocha.getRunner().suite.ctx.currentTest.parent.title;
const testName = Cypress.mocha.getRunner().suite.ctx.currentTest.title;

Expand All @@ -43,7 +43,10 @@ export const afterEach = (taskResult, backend = 'github') => {
testName,
});

if (Cypress.mocha.getRunner().suite.ctx.currentTest.state === 'failed') {
if (
!process.env.RECORD_FIXTURES &&
Cypress.mocha.getRunner().suite.ctx.currentTest.state === 'failed'
) {
Cypress.runner.stop();
}
};
30 changes: 30 additions & 0 deletions cypress/integration/editorial_workflow_spec_bitbucket_backend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'bitbucket';

describe('BitBucket Backend Editorial Workflow', () => {
let taskResult = { data: {} };

before(() => {
specUtils.before(taskResult, { publish_mode: 'editorial_workflow' }, backend);
});

after(() => {
specUtils.after(taskResult, backend);
});

beforeEach(() => {
specUtils.beforeEach(taskResult, backend);
});

afterEach(() => {
specUtils.afterEach(taskResult, backend);
});

fixture({
entries: [entry1, entry2, entry3],
getUser: () => taskResult.data.user,
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import fixture from './common/editorial_workflow';
import * as specUtils from './common/spec_utils';
import { entry1, entry2, entry3 } from './common/entries';

const backend = 'git-gateway';
const provider = 'github';

describe('Git Gateway (GitHub) Backend Editorial Workflow', () => {
let taskResult = { data: {} };

before(() => {
specUtils.before(taskResult, { publish_mode: 'editorial_workflow', provider }, backend);
});

after(() => {
specUtils.after(taskResult, backend);
});

beforeEach(() => {
specUtils.beforeEach(taskResult, backend);
});

afterEach(() => {
specUtils.afterEach(taskResult, backend);
});

fixture({
entries: [entry1, entry2, entry3],
getUser: () => taskResult.data.user,
});
});
Loading

0 comments on commit 6f221ab

Please sign in to comment.