diff --git a/.github/workflows/pull-req.yml b/.github/workflows/pull-req.yml index b3844939f..cf0af735f 100644 --- a/.github/workflows/pull-req.yml +++ b/.github/workflows/pull-req.yml @@ -15,7 +15,7 @@ jobs: github.event_name == 'pull_request' && ( github.event.action == 'review_requested' || github.base_ref == 'master' || - (github.base_ref == 'dev' && ( + ((github.base_ref == 'dev' || github.base_ref == 'dev-flex-2024') && ( github.event.pull_request.draft == false || startsWith(github.head_ref, 'dependabot/') )) diff --git a/__tests__/end-to-end.js b/__tests__/end-to-end.js index 267728edd..e8bfea96a 100644 --- a/__tests__/end-to-end.js +++ b/__tests__/end-to-end.js @@ -768,6 +768,19 @@ async function getInnerHTMLFromSelector (selector: string) { return stripReactTags(html) } +/** + * Gets the id of a given selector. + */ +async function getIdFromSelector (selector: string) { + log.info(`getting id for selector: ${selector}`) + const id = (await page.$eval(selector, el => { + const _el = (el: any) + // make flow happy cause flow-typed page.$eval doesn't get specifc enough + return _el.id + }): any) + return id +} + /** * Gets all the element handles found using the given selector. */ @@ -1070,7 +1083,7 @@ describe('end-to-end', () => { await createProject(testProjectToDeleteName) // go back to project list - await goto('http://localhost:9966/project', {waitUntil: 'networkidle0'}) + await goto('https://datatools-ui-proxy/project', {waitUntil: 'networkidle0'}) // get the created project id // go into the project page and verify that it looks ok-ish @@ -1186,6 +1199,143 @@ describe('end-to-end', () => { await expectFeedVersionValidityDates('Apr 8, 2018', 'Jun 30, 2018') }, defaultTestTimeout) + makeTestPostFeedSource('should be able to download and validate snapshot gtfs', async () => { + // go to main feed tab + await waitForAndClick('#feed-source-viewer-tabs-tab-') + await wait(5000, 'additional time for page to load') + + // wait for main tab to show up with version validity info + await waitForSelector('[data-test-id="active-feed-version-validity-start"]') + + // click load version button + await click('[data-test-id="load-feed-version-button"]') + await waitForAndClick('[data-test-id="modal-confirm-ok-button"]') + + // wait for load to finish + await waitAndClearCompletedJobs() + + // wait for page to be visible and go to snapshots tab + await waitForAndClick('#feed-source-viewer-tabs-tab-snapshots') + await wait(2000, 'for page to load?') + + // wait for snapshots tab to load and create snapshot + await waitForAndClick('[data-test-id="snapshot-latest-changes-button"]') + + // wait for snapshot export modal and click "no" to proprietary file export + await waitForAndClick('[data-test-id="confirm-snapshot-create-button"]') + + // wait for version to get created + await waitAndClearCompletedJobs() + + // the page has to reload to show the new snapshot + // TODO: Fix? + await page.reload({ waitUntil: 'networkidle0' }) + + // Download version + await waitForAndClick('[data-test-id="download-snapshot-button"]') + await wait(15000, 'for file to download') + + const fileName = await getIdFromSelector('[data-test-id="download-snapshot-button"]') + // file should get saved to the current root directory, go looking for it + // verify that file exists + const downloadsDir = './' + // $FlowFixMe old version of flow doesn't know latest fs methods + const files = await fs.readdir(downloadsDir) + log.info(fileName) + log.info(files) + + let feedVersionDownloadFile = '' + // assume that this file will be the first zip file download + for (const file of files) { + if (file.indexOf(fileName.replace(/:/g, '')) > -1) { + feedVersionDownloadFile = file + break + } + } + if (!feedVersionDownloadFile) { + throw new Error('Feed Version gtfs file not found in Downloads folder!') + } + + const filePath = path.join(downloadsDir, feedVersionDownloadFile) + + // go to main feed tab + await waitForAndClick('#feed-source-viewer-tabs-tab-') + await wait(5000, 'additional time for page to load') + + // wait for main tab to show up with version validity info + await waitForSelector('[data-test-id="active-feed-version-validity-start"]') + + const origDtErrorCount = await getInnerHTMLFromSelector('[data-test-id="dt-error-count-label"]') + const origMdErrorCount = await getInnerHTMLFromSelector('[data-test-id="md-error-count-label"]') + + // Open dropdown + await waitForAndClick( + '#bg-nested-dropdown', + { visible: true } + ) + // create new version by fetching + await waitForAndClick( + '[data-test-id="upload-feed-button"]', + { visible: true } + ) + + // upload file to form + const fileUploadForm = await page.$('input[type=file]') + await fileUploadForm.uploadFile(filePath) + + // click "OK to upload file + await waitForAndClick('[data-test-id="upload-gtfs-modal-ok"]') + // wait for version to get created + await waitAndClearCompletedJobs() + + // delete file + // $FlowFixMe old version of flow doesn't know latest fs methods + await fs.remove(filePath) + + // go to main feed tab + await click('#feed-source-viewer-tabs-tab-') + + // wait for main tab to show up with version validity info + await waitForSelector('[data-test-id="active-feed-version-validity-start"]') + + // verify that snapshot was made active version + await expectFeedVersionValidityDates('May 29, 2018', 'May 29, 2028') + + // open validation results + await click('[data-test-id="validation-issues-link"]') + await waitForSelector('[data-test-id="dt-error-count-label"]') + + const newDtErrorCount = await getInnerHTMLFromSelector('[data-test-id="dt-error-count-label"]') + const newMdErrorCount = await getInnerHTMLFromSelector('[data-test-id="md-error-count-label"]') + + expect(newDtErrorCount).toEqual(origDtErrorCount) + expect(newMdErrorCount).toEqual(origMdErrorCount) + + // todo: check rest of validation results for correct counts + + // go to main feed tab + await click('#feed-source-viewer-tabs-tab-') + + // wait for main tab to show up with version validity info + await waitForSelector('[data-test-id="active-feed-version-validity-start"]') + + // remove version and snapshot + await waitForAndClick('[data-test-id="delete-feed-version-button"]') + // confirm action in modal + await waitForAndClick('[data-test-id="modal-confirm-ok-button"]') + await wait(2000, 'for data to refresh') + await waitForSelector('#feed-source-viewer-tabs') + + // wait for page to be visible and go to snapshots tab + await waitForAndClick('#feed-source-viewer-tabs-tab-snapshots') + await wait(2000, 'for page to load?') + + // wait for snapshots tab to load and delete snapshot + await waitForAndClick('[data-test-id="delete-snapshot-button"]') + await waitForAndClick('[data-test-id="modal-confirm-ok-button"]') + await wait(2000, 'for page to load?') + }, defaultTestTimeout) + if (doNonEssentialSteps) { makeTestPostFeedSource('should delete feed source', async () => { const testFeedSourceToDeleteName = `test-feed-source-to-delete-${testTime}` @@ -2684,6 +2834,62 @@ describe('end-to-end', () => { ) }, defaultTestTimeout, 'should create trip') + makeEditorEntityTest('should create trip data via series editor', async () => { + // open series editor + await click('[data-test-id="create-trip-series-button"]') + + // wait for modal to appear + await waitForSelector( + '#hours' + ) + + // click hours field + await click('#hours:nth-child(1)') + + // enter hours + await page.keyboard.type('13') + + // tab to headway + await page.keyboard.press('Tab') + await page.keyboard.press('Tab') + await page.keyboard.press('Tab') + + // enter minutes + await page.keyboard.type('5') + + // tab to end time + await page.keyboard.press('Tab') + + // enter hours + await page.keyboard.type('15') + + // submit + await click('[data-test-id="generate-trips-button"]') + + // wait for save to happen + await wait(2000, 'for save to happen') + + // save + await click('[data-test-id="save-trip-button"]') + + // wait for save to happen + await wait(2000, 'for save to happen') + + // reload to make sure stuff was saved + await page.reload({ waitUntil: 'networkidle0' }) + + // wait for timetable to appear + await waitForSelector( + '[data-test-id="timetable-area"]' + ) + + // verify data was generated correctly and retrieved from server + await expectSelectorToContainHtml( + '[data-test-id="timetable-area"]', + '13:05:00' + ) + }, defaultTestTimeout, 'should create trip') + makeEditorEntityTest('should delete trip data', async () => { // create a new trip that will get deleted await click('[data-test-id="duplicate-trip-button"]') diff --git a/docs/user/otp-deployment.md b/docs/user/otp-deployment.md index 5bba202c6..516782f60 100644 --- a/docs/user/otp-deployment.md +++ b/docs/user/otp-deployment.md @@ -18,3 +18,6 @@ The deployment architecture diagram below depicts how OTP servers are managed by 1. [Setting up OTP UI and backend servers on AWS](./setting-up-aws-servers.md) 2. [Adding a deployment server from TRANSIT-data-tools](./add-deployment-server.md) 3. [Deploying GTFS feeds to OTP](./deploying-feeds.md) + +#### Warning: +Datatools uses the [OTP Actuator API](https://docs.opentripplanner.org/en/latest/sandbox/ActuatorAPI) to determine if OTP started correctly. If you do not enable this API, the deployment will not succeed. \ No newline at end of file diff --git a/lib/common/components/SelectFileModal.js b/lib/common/components/SelectFileModal.js index 1816e4a7f..c36955966 100644 --- a/lib/common/components/SelectFileModal.js +++ b/lib/common/components/SelectFileModal.js @@ -105,7 +105,7 @@ export default class SelectFileModal extends Component { diff --git a/lib/editor/components/EditorFeedSourcePanel.js b/lib/editor/components/EditorFeedSourcePanel.js index 7c40ae23e..d09133641 100644 --- a/lib/editor/components/EditorFeedSourcePanel.js +++ b/lib/editor/components/EditorFeedSourcePanel.js @@ -111,6 +111,7 @@ export default class EditorFeedSourcePanel extends Component {