[gold] Port changelists-page-sk to TypeScript.
Bug: skia:10246
Change-Id: I3f9c0ac21a271974542902a831ca9f371669bd5f
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/399536
Commit-Queue: Leandro Lovisolo <lovisolo@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/golden/go/web/frontend/generate_typescript_rpc_types/main.go b/golden/go/web/frontend/generate_typescript_rpc_types/main.go
index bc2e1b3..f6c8778 100644
--- a/golden/go/web/frontend/generate_typescript_rpc_types/main.go
+++ b/golden/go/web/frontend/generate_typescript_rpc_types/main.go
@@ -61,6 +61,9 @@
// Response for the /json/v1/triagelog RPC endpoint.
generator.Add(frontend.TriageLogResponse{})
+ // Response for the /json/v1/changelists RPC endpoint.
+ generator.Add(frontend.ChangelistsResponse{})
+
generator.AddUnionWithName(expectations.AllLabel, "Label")
generator.AddUnionWithName(common.AllRefClosest, "RefClosest")
}
diff --git a/golden/go/web/frontend/types.go b/golden/go/web/frontend/types.go
index 9bf4c96..a5d009a 100644
--- a/golden/go/web/frontend/types.go
+++ b/golden/go/web/frontend/types.go
@@ -41,6 +41,12 @@
URL string `json:"url"`
}
+// ChangelistsResponse is the response for /json/v1/changelists.
+type ChangelistsResponse struct {
+ Changelists []Changelist `json:"changelists"`
+ httputils.ResponsePagination
+}
+
// ConvertChangelist turns a code_review.Changelist into a Changelist for the frontend.
func ConvertChangelist(cl code_review.Changelist, system, urlTempl string) Changelist {
return Changelist{
diff --git a/golden/go/web/helpers.go b/golden/go/web/helpers.go
index 84058a0..7378a4f 100644
--- a/golden/go/web/helpers.go
+++ b/golden/go/web/helpers.go
@@ -23,14 +23,6 @@
noSniffContent = "nosniff"
)
-// ResponseEnvelope wraps all responses. Some fields might be empty depending
-// on context or whether there was an error or not.
-type ResponseEnvelope struct {
- Data *interface{} `json:"data"`
- Status int `json:"status"`
- Pagination *httputils.ResponsePagination `json:"pagination"`
-}
-
// setJSONHeaders sets secure headers for JSON responses.
func setJSONHeaders(w http.ResponseWriter) {
h := w.Header()
@@ -39,22 +31,6 @@
h.Set(contentTypeOptionsHeader, noSniffContent)
}
-// sendResponseWithPagination wraps the data of a successful response in a response envelope
-// and sends it to the client.
-func sendResponseWithPagination(w http.ResponseWriter, data interface{}, pagination *httputils.ResponsePagination) {
- resp := ResponseEnvelope{
- Data: &data,
- Status: http.StatusOK,
- Pagination: pagination,
- }
- setJSONHeaders(w)
- w.WriteHeader(http.StatusOK)
- if err := json.NewEncoder(w).Encode(resp); err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-}
-
// sendJSONResponse serializes resp to JSON. If an error occurs
// a text based error code is send to the client.
func sendJSONResponse(w http.ResponseWriter, resp interface{}) {
diff --git a/golden/go/web/web.go b/golden/go/web/web.go
index e7de13c..3816dd5 100644
--- a/golden/go/web/web.go
+++ b/golden/go/web/web.go
@@ -384,7 +384,12 @@
return
}
- sendResponseWithPagination(w, cls, pagination)
+ response := frontend.ChangelistsResponse{
+ Changelists: cls,
+ ResponsePagination: *pagination,
+ }
+
+ sendJSONResponse(w, response)
}
// getIngestedChangelists performs the core of the logic for ChangelistsHandler,
diff --git a/golden/go/web/web_test.go b/golden/go/web/web_test.go
index 3d32c11..de952e6 100644
--- a/golden/go/web/web_test.go
+++ b/golden/go/web/web_test.go
@@ -321,12 +321,16 @@
mcls := &mock_clstore.Store{}
defer mcls.AssertExpectations(t)
+ const offset = 0
+ const size = 50
+
mcls.On("GetChangelists", testutils.AnyContext, clstore.SearchOptions{
- StartIdx: 0,
- Limit: 50,
+ StartIdx: offset,
+ Limit: size,
}).Return(makeCodeReviewCLs(), len(makeCodeReviewCLs()), nil)
wh := Handlers{
+ anonymousExpensiveQuota: rate.NewLimiter(rate.Inf, 1),
HandlersConfig: HandlersConfig{
ReviewSystems: []clstore.ReviewSystem{
{
@@ -338,19 +342,23 @@
},
}
- cls, pagination, err := wh.getIngestedChangelists(context.Background(), 0, 50, false)
- assert.NoError(t, err)
- assert.Len(t, cls, 3)
- assert.NotNil(t, pagination)
+ cls := makeWebCLs()
- assert.Equal(t, &httputils.ResponsePagination{
- Offset: 0,
- Size: 50,
- Total: 3,
- }, pagination)
+ expectedResponse := frontend.ChangelistsResponse{
+ Changelists: cls,
+ ResponsePagination: httputils.ResponsePagination{
+ Offset: offset,
+ Size: size,
+ Total: len(cls),
+ },
+ }
- expected := makeWebCLs()
- assert.Equal(t, expected, cls)
+ w := httptest.NewRecorder()
+ r := httptest.NewRequest(http.MethodGet, "/json/v1/changelist?size=50", nil)
+ wh.ChangelistsHandler(w, r)
+ b, err := json.Marshal(expectedResponse)
+ require.NoError(t, err)
+ assertJSONResponseWas(t, http.StatusOK, string(b), w)
}
// TestGetIngestedChangelists_ActiveChangelists_SunnyDay_Success makes sure that we properly get
@@ -361,13 +369,17 @@
mcls := &mock_clstore.Store{}
defer mcls.AssertExpectations(t)
+ const offset = 20
+ const size = 30
+
mcls.On("GetChangelists", testutils.AnyContext, clstore.SearchOptions{
- StartIdx: 20,
- Limit: 30,
+ StartIdx: offset,
+ Limit: size,
OpenCLsOnly: true,
}).Return(makeCodeReviewCLs(), 3, nil)
wh := Handlers{
+ anonymousExpensiveQuota: rate.NewLimiter(rate.Inf, 1),
HandlersConfig: HandlersConfig{
ReviewSystems: []clstore.ReviewSystem{
{
@@ -379,19 +391,23 @@
},
}
- cls, pagination, err := wh.getIngestedChangelists(context.Background(), 20, 30, true)
- assert.NoError(t, err)
- assert.Len(t, cls, 3)
- assert.NotNil(t, pagination)
+ cls := makeWebCLs()
- assert.Equal(t, &httputils.ResponsePagination{
- Offset: 20,
- Size: 30,
- Total: 3,
- }, pagination)
+ expectedResponse := frontend.ChangelistsResponse{
+ Changelists: cls,
+ ResponsePagination: httputils.ResponsePagination{
+ Offset: offset,
+ Size: size,
+ Total: len(cls),
+ },
+ }
- expected := makeWebCLs()
- assert.Equal(t, expected, cls)
+ w := httptest.NewRecorder()
+ r := httptest.NewRequest(http.MethodGet, "/json/v1/changelist?offset=20&size=30&active=true", nil)
+ wh.ChangelistsHandler(w, r)
+ b, err := json.Marshal(expectedResponse)
+ require.NoError(t, err)
+ assertJSONResponseWas(t, http.StatusOK, string(b), w)
}
func makeCodeReviewCLs() []code_review.Changelist {
diff --git a/golden/modules/changelists-page-sk/changelists-page-sk-demo.js b/golden/modules/changelists-page-sk/changelists-page-sk-demo.ts
similarity index 60%
rename from golden/modules/changelists-page-sk/changelists-page-sk-demo.js
rename to golden/modules/changelists-page-sk/changelists-page-sk-demo.ts
index cdaa767..a1864fb 100644
--- a/golden/modules/changelists-page-sk/changelists-page-sk-demo.js
+++ b/golden/modules/changelists-page-sk/changelists-page-sk-demo.ts
@@ -3,7 +3,6 @@
import fetchMock from 'fetch-mock';
import { deepCopy } from 'common-sk/modules/object';
-import { $$ } from 'common-sk/modules/dom';
import { delay } from '../demo_util';
import { fakeNow, changelistSummaries_5, empty } from './test_data';
import { testOnlySetSettings } from '../settings';
@@ -17,40 +16,36 @@
Date.now = () => fakeNow;
const ten = deepCopy(changelistSummaries_5);
-ten.data.push(...ten.data);
-ten.pagination = {
- offset: 0,
- size: 10,
- total: 2147483647,
-};
+ten.changelists!.push(...ten.changelists!);
+ten.offset = 0;
+ten.size = 10;
+ten.total = 2147483647;
-changelistSummaries_5.pagination = {
- offset: 10,
- size: 10,
- total: 15,
-};
+changelistSummaries_5.offset = 10;
+changelistSummaries_5.size = 10;
+changelistSummaries_5.total = 15;
const open = deepCopy(changelistSummaries_5);
-open.data = open.data.slice(0, 3);
-open.pagination = {
- offset: 0,
- size: 3,
- total: 3,
-};
+open.changelists = open.changelists!.slice(0, 3);
+open.offset = 0;
+open.size = 3;
+open.total = 3;
const fakeRpcDelayMillis = 300;
fetchMock.get('/json/v1/changelists?offset=0&size=10', delay(ten, fakeRpcDelayMillis));
-fetchMock.get('/json/v1/changelists?offset=0&size=10&active=true', delay(open, fakeRpcDelayMillis));
-fetchMock.get('/json/v1/changelists?offset=10&size=10', delay(changelistSummaries_5, fakeRpcDelayMillis));
-fetchMock.get('glob:/json/v1/changelists*', delay(empty, fakeRpcDelayMillis));
+fetchMock.get(
+ '/json/v1/changelists?offset=0&size=10&active=true', delay(open, fakeRpcDelayMillis));
+fetchMock.get(
+ '/json/v1/changelists?offset=10&size=10', delay(changelistSummaries_5, fakeRpcDelayMillis));
+fetchMock.get('glob:/json/v1/changelists*', delay(empty(), fakeRpcDelayMillis));
fetchMock.get('/json/v1/trstatus', JSON.stringify(exampleStatusData));
// By adding these elements after all the fetches are mocked out, they should load ok.
const newScaf = document.createElement('gold-scaffold-sk');
newScaf.setAttribute('testing_offline', 'true');
-const body = $$('body');
-body.insertBefore(newScaf, body.childNodes[0]); // Make it the first element in body.
+// Make it the first element in body.
+document.body.insertBefore(newScaf, document.body.childNodes[0]);
const page = document.createElement('changelists-page-sk');
page.setAttribute('page_size', '10');
newScaf.appendChild(page);
diff --git a/golden/modules/changelists-page-sk/changelists-page-sk.js b/golden/modules/changelists-page-sk/changelists-page-sk.js
deleted file mode 100644
index 79fec2f..0000000
--- a/golden/modules/changelists-page-sk/changelists-page-sk.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- * @module modules/changelists-page-sk
- * @description <h2><code>changelists-page-sk</code></h2>
- *
- * Allows the user to page through the ChangeLists for which Gold has seen
- * data uploaded via TryJobs.
- *
- */
-import * as human from 'common-sk/modules/human';
-
-import { define } from 'elements-sk/define';
-import { html } from 'lit-html';
-import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
-import { stateReflector } from 'common-sk/modules/stateReflector';
-import { ElementSk } from '../../../infra-sk/modules/ElementSk';
-
-import 'elements-sk/checkbox-sk';
-import 'elements-sk/icon/block-icon-sk';
-import 'elements-sk/icon/cached-icon-sk';
-import 'elements-sk/icon/done-icon-sk';
-
-import '../pagination-sk';
-import { sendBeginTask, sendEndTask, sendFetchError } from '../common';
-
-const _statusIcon = (cl) => {
- if (cl.status === 'Open') {
- return html`<cached-icon-sk title="ChangeList is open"></cached-icon-sk>`;
- } if (cl.status === 'Landed') {
- return html`<done-icon-sk title="ChangeList was landed"></done-icon-sk>`;
- }
- return html`<block-icon-sk title="ChangeList was abandoned"></block-icon-sk>`;
-};
-
-const changelist = (cl) => html`
-<tr>
- <td class=id>
- <a title="See codereview in a new window" target=_blank rel=noopener href=${cl.url}
- >${cl.id}</a>
- ${_statusIcon(cl)}
- </td>
- <td>
- <a href="/cl/${cl.system}/${cl.id}"
- target="_blank" rel="noopener">Triage</a>
- </td>
- <td class=owner>${cl.owner}</td>
- <td title=${cl.updated}>${human.diffDate(cl.updated)} ago</td>
- <td>${cl.subject}</td>
-</tr>`;
-
-const template = (ele) => html`
-<div class=controls>
- <pagination-sk page_size=${ele._page_size} offset=${ele._offset}
- total=${ele._total} @page-changed=${ele._pageChanged}>
- </pagination-sk>
-
- <checkbox-sk label="show all" ?checked=${ele._showAll}
- @click=${ele._toggleShowAll}></checkbox-sk>
-</div>
-
-<table>
- <thead>
- <tr>
- <th colspan=2>ChangeList</th>
- <th>Owner</th>
- <th>Updated</th>
- <th>Subject</th>
- </tr>
- </thead>
- <tbody>
- ${ele._cls.map(changelist)}
-</tbody>`;
-
-define('changelists-page-sk', class extends ElementSk {
- constructor() {
- super(template);
-
- // Set empty values to allow empty rendering while we wait for
- // stateReflector (which triggers on DomReady). Additionally, these values
- // help stateReflector with types.
- this._cls = [];
- this._offset = 0;
- this._page_size = 0;
- this._total = 0;
- this._showAll = false;
-
- this._stateChanged = stateReflector(
- /* getState */() => ({
- // provide empty values
- offset: this._offset,
- page_size: this._page_size,
- show_all: this._showAll,
- }), /* setState */(newState) => {
- if (!this._connected) {
- return;
- }
-
- // default values if not specified.
- this._offset = newState.offset || 0;
- this._page_size = newState.page_size || +this.getAttribute('page_size') || 50;
- this._showAll = newState.show_all || false;
- this._fetch();
- this._render();
- },
- );
-
- // Allows us to abort fetches if a user pages.
- this._fetchController = null;
- }
-
- connectedCallback() {
- super.connectedCallback();
- this._render();
- }
-
- // Returns a promise that resolves when all outstanding requests resolve
- // or null if none were made. This promise makes unit tests a little more concise.
- _fetch() {
- if (this._fetchController) {
- // Kill any outstanding requests
- this._fetchController.abort();
- }
-
- // Make a fresh abort controller for each set of fetches.
- // They cannot be re-used once aborted.
- this._fetchController = new AbortController();
- const extra = {
- signal: this._fetchController.signal,
- };
-
- sendBeginTask(this);
- let u = `/json/v1/changelists?offset=${this._offset}&size=${this._page_size}`;
- if (!this._showAll) {
- u += '&active=true';
- }
- return fetch(u, extra)
- .then(jsonOrThrow)
- .then((json) => {
- this._cls = json.data || [];
- this._offset = json.pagination.offset;
- this._total = json.pagination.total;
- this._render();
- sendEndTask(this);
- })
- .catch((e) => sendFetchError(this, e, 'changelists'));
- }
-
- _pageChanged(e) {
- const d = e.detail;
- this._offset += d.delta * this._page_size;
- if (this._offset < 0) {
- this._offset = 0;
- }
- this._stateChanged();
- this._render();
- this._fetch();
- }
-
- _toggleShowAll(e) {
- e.preventDefault();
- this._showAll = !this._showAll;
- this._stateChanged();
- this._render();
- this._fetch();
- }
-});
diff --git a/golden/modules/changelists-page-sk/changelists-page-sk.ts b/golden/modules/changelists-page-sk/changelists-page-sk.ts
new file mode 100644
index 0000000..380a8c9
--- /dev/null
+++ b/golden/modules/changelists-page-sk/changelists-page-sk.ts
@@ -0,0 +1,173 @@
+/**
+ * @module modules/changelists-page-sk
+ * @description <h2><code>changelists-page-sk</code></h2>
+ *
+ * Allows the user to page through the ChangeLists for which Gold has seen
+ * data uploaded via TryJobs.
+ *
+ */
+import * as human from 'common-sk/modules/human';
+
+import { define } from 'elements-sk/define';
+import { html } from 'lit-html';
+import { jsonOrThrow } from 'common-sk/modules/jsonOrThrow';
+import { stateReflector } from 'common-sk/modules/stateReflector';
+import { ElementSk } from '../../../infra-sk/modules/ElementSk';
+
+import 'elements-sk/checkbox-sk';
+import 'elements-sk/icon/block-icon-sk';
+import 'elements-sk/icon/cached-icon-sk';
+import 'elements-sk/icon/done-icon-sk';
+
+import '../pagination-sk';
+import { sendBeginTask, sendEndTask, sendFetchError } from '../common';
+import { Changelist, ChangelistsResponse } from '../rpc_types';
+import { PaginationSkPageChangedEventDetail } from '../pagination-sk/pagination-sk';
+
+export class ChangelistsPageSk extends ElementSk {
+ private static template = (ele: ChangelistsPageSk) => html`
+ <div class=controls>
+ <pagination-sk page_size=${ele.pageSize} offset=${ele.offset}
+ total=${ele.total} @page-changed=${ele.pageChanged}>
+ </pagination-sk>
+
+ <checkbox-sk label="show all" ?checked=${ele.showAll}
+ @click=${ele.toggleShowAll}></checkbox-sk>
+ </div>
+
+ <table>
+ <thead>
+ <tr>
+ <th colspan=2>ChangeList</th>
+ <th>Owner</th>
+ <th>Updated</th>
+ <th>Subject</th>
+ </tr>
+ </thead>
+ <tbody>
+ ${ele.cls.map(ChangelistsPageSk.changelist)}
+ </tbody>
+ `;
+
+ private static changelist = (cl: Changelist) => html`
+ <tr>
+ <td class=id>
+ <a title="See codereview in a new window" target=_blank rel=noopener href=${cl.url}
+ >${cl.id}</a>
+ ${ChangelistsPageSk.statusIcon(cl)}
+ </td>
+ <td>
+ <a href="/cl/${cl.system}/${cl.id}"
+ target="_blank" rel="noopener">Triage</a>
+ </td>
+ <td class=owner>${cl.owner}</td>
+ <td title=${cl.updated}>${human.diffDate(cl.updated)} ago</td>
+ <td>${cl.subject}</td>
+ </tr>
+ `;
+
+ private static statusIcon = (cl: Changelist) => {
+ if (cl.status === 'Open') {
+ return html`<cached-icon-sk title="ChangeList is open"></cached-icon-sk>`;
+ } if (cl.status === 'Landed') {
+ return html`<done-icon-sk title="ChangeList was landed"></done-icon-sk>`;
+ }
+ return html`<block-icon-sk title="ChangeList was abandoned"></block-icon-sk>`;
+ };
+
+ // Set empty values to allow empty rendering while we wait for
+ // stateReflector (which triggers on DomReady). Additionally, these values
+ // help stateReflector with types.
+ private cls: Changelist[] = [];
+ private offset = 0;
+ private pageSize = 0;
+ private total = 0;
+ private showAll = false;
+
+ private readonly stateChanged: () => void;
+
+ // Allows us to abort fetches if a user pages.
+ private fetchController?: AbortController;
+
+ constructor() {
+ super(ChangelistsPageSk.template);
+ this.stateChanged = stateReflector(
+ /* getState */() => ({
+ // provide empty values
+ offset: this.offset,
+ page_size: this.pageSize,
+ show_all: this.showAll,
+ }), /* setState */(newState) => {
+ if (!this._connected) {
+ return;
+ }
+
+ // default values if not specified.
+ this.offset = newState.offset as number || 0;
+ this.pageSize =
+ newState.page_size as number || +this.getAttribute('page_size')! || 50;
+ this.showAll = newState.show_all as boolean || false;
+ this.fetch();
+ this._render();
+ },
+ );
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this._render();
+ }
+
+ // Returns a promise that resolves when all outstanding requests resolve
+ // or null if none were made. This promise makes unit tests a little more concise.
+ private fetch(): Promise<void> {
+ if (this.fetchController) {
+ // Kill any outstanding requests
+ this.fetchController.abort();
+ }
+
+ // Make a fresh abort controller for each set of fetches.
+ // They cannot be re-used once aborted.
+ this.fetchController = new AbortController();
+ const extra = {
+ signal: this.fetchController.signal,
+ };
+
+ sendBeginTask(this);
+ let u = `/json/v1/changelists?offset=${this.offset}&size=${this.pageSize}`;
+ if (!this.showAll) {
+ u += '&active=true';
+ }
+ return fetch(u, extra)
+ .then(jsonOrThrow)
+ .then((response: ChangelistsResponse) => {
+ this.cls = response.changelists || [];
+ this.offset = response.offset;
+ this.total = response.total;
+ this._render();
+ sendEndTask(this);
+ })
+ .catch((e) => sendFetchError(this, e, 'changelists'));
+ }
+
+ private pageChanged(e: CustomEvent<PaginationSkPageChangedEventDetail>) {
+ const d = e.detail;
+ this.offset += d.delta * this.pageSize;
+ if (this.offset < 0) {
+ this.offset = 0;
+ }
+ this.stateChanged();
+ this._render();
+ this.fetch();
+ }
+
+ private toggleShowAll(e: Event) {
+ e.preventDefault();
+ this.showAll = !this.showAll;
+ this.stateChanged();
+ this._render();
+ this.fetch();
+ }
+}
+
+define('changelists-page-sk', ChangelistsPageSk);
diff --git a/golden/modules/changelists-page-sk/changelists-page-sk_test.js b/golden/modules/changelists-page-sk/changelists-page-sk_test.ts
similarity index 65%
rename from golden/modules/changelists-page-sk/changelists-page-sk_test.js
rename to golden/modules/changelists-page-sk/changelists-page-sk_test.ts
index b8e0ed2..837342d 100644
--- a/golden/modules/changelists-page-sk/changelists-page-sk_test.js
+++ b/golden/modules/changelists-page-sk/changelists-page-sk_test.ts
@@ -14,9 +14,11 @@
setQueryString,
setUpElementUnderTest,
} from '../../../infra-sk/modules/test_util';
+import { ChangelistsPageSk } from './changelists-page-sk';
+import { expect } from 'chai';
describe('changelists-page-sk', () => {
- const newInstance = setUpElementUnderTest('changelists-page-sk');
+ const newInstance = setUpElementUnderTest<ChangelistsPageSk>('changelists-page-sk');
// Instantiate page; wait for RPCs to complete and for the page to render.
const loadChangelistsPageSk = async () => {
@@ -29,20 +31,22 @@
beforeEach(async () => {
// Clear out any query params we might have to not mess with our current state.
setQueryString('');
-
- // These are the default offset/page_size params
- fetchMock.get('/json/v1/changelists?offset=0&size=50&active=true', changelistSummaries_5);
});
afterEach(() => {
+ expect(fetchMock.done()).to.be.true; // All mock RPCs called at least once.
// Completely remove the mocking which allows each test
// to be able to mess with the mocked routes w/o impacting other tests.
fetchMock.reset();
});
describe('html layout', () => {
- let changelistsPageSk;
+ let changelistsPageSk: ChangelistsPageSk
+
beforeEach(async () => {
+ // These are the default offset/page_size params
+ fetchMock.get('/json/v1/changelists?offset=0&size=50&active=true', changelistSummaries_5);
+
changelistsPageSk = await loadChangelistsPageSk();
});
@@ -68,48 +72,37 @@
}); // end describe('html layout')
describe('api calls', () => {
- let changelistsPageSk;
- beforeEach(async () => {
- changelistsPageSk = await loadChangelistsPageSk();
- });
-
it('includes pagination params in request to changelists', async () => {
- fetchMock.resetHistory();
-
- fetchMock.get('/json/v1/changelists?offset=100&size=10', empty);
- // pretend these were loaded in via stateReflector
- changelistsPageSk._offset = 100;
- changelistsPageSk._page_size = 10;
- changelistsPageSk._showAll = true;
-
- await changelistsPageSk._fetch();
+ setQueryString('?offset=100&page_size=10');
+ fetchMock.get('/json/v1/changelists?offset=100&size=10&active=true', empty);
+ await loadChangelistsPageSk()
});
it('includes the active params unless show_all is set', async () => {
- fetchMock.resetHistory();
-
- fetchMock.get('/json/v1/changelists?offset=100&size=10&active=true', empty);
- // pretend these were loaded in via stateReflector
- changelistsPageSk._offset = 100;
- changelistsPageSk._page_size = 10;
- changelistsPageSk._showAll = false;
-
- await changelistsPageSk._fetch();
+ setQueryString('?offset=100&page_size=10&show_all=true');
+ fetchMock.get('/json/v1/changelists?offset=100&size=10', empty);
+ await loadChangelistsPageSk()
});
}); // end describe('api calls')
describe('navigation', () => {
it('responds to the browser back/forward buttons', async () => {
+ // These are the default offset/page_size params. This request will be made when we test the
+ // ?hello=world query string.
+ fetchMock.get('/json/v1/changelists?offset=0&size=50&active=true', changelistSummaries_5);
+
// First page of results.
fetchMock.get(
'/json/v1/changelists?offset=0&size=5&active=true',
changelistSummaries_5,
);
+
// Second page of results.
fetchMock.get(
'/json/v1/changelists?offset=5&size=5&active=true',
changelistSummaries_5_offset5,
);
+
// Third page of results.
fetchMock.get(
'/json/v1/changelists?offset=10&size=5&active=true',
@@ -128,24 +121,24 @@
// Instantiate component.
const changelistsPageSk = await loadChangelistsPageSk();
expectQueryStringToEqual('?page_size=5');
- expectFirstPage();
+ expectFirstPage(changelistsPageSk);
await goToNextPage(changelistsPageSk);
expectQueryStringToEqual('?offset=5&page_size=5');
- expectSecondPage();
+ expectSecondPage(changelistsPageSk);
await goToNextPage(changelistsPageSk);
expectQueryStringToEqual('?offset=10&page_size=5');
- expectThirdPage();
+ expectThirdPage(changelistsPageSk);
await goBack();
expectQueryStringToEqual('?offset=5&page_size=5');
- expectSecondPage();
+ expectSecondPage(changelistsPageSk);
// State at component instantiation.
await goBack();
expectQueryStringToEqual('?page_size=5');
- expectFirstPage();
+ expectFirstPage(changelistsPageSk);
// State before the component was instantiated.
await goBack();
@@ -153,48 +146,44 @@
await goForward();
expectQueryStringToEqual('?page_size=5');
- expectFirstPage();
+ expectFirstPage(changelistsPageSk);
await goForward();
expectQueryStringToEqual('?offset=5&page_size=5');
- expectSecondPage();
+ expectSecondPage(changelistsPageSk);
await goForward();
expectQueryStringToEqual('?offset=10&page_size=5');
- expectThirdPage();
+ expectThirdPage(changelistsPageSk);
});
}); // end describe('navigation')
describe('dynamic content', () => {
- let changelistsPageSk;
- beforeEach(async () => {
- changelistsPageSk = await loadChangelistsPageSk();
- });
+ it('responds to clicking the show all checkbox', async () => {
+ fetchMock.get('/json/v1/changelists?offset=0&size=5&active=true', empty);
+ fetchMock.get('/json/v1/changelists?offset=0&size=5', empty);
- it('responds to clicking the show all checkbox', () => {
- fetchMock.get('/json/v1/changelists?offset=0&size=50', empty);
+ setQueryString('?page_size=5');
+ const changelistsPageSk = await loadChangelistsPageSk();
+
// click on the input inside the checkbox, otherwise, we see double
// events, since checkbox-sk "re-throws" the click event.
- const showAllBox = $$('.controls checkbox-sk input', changelistsPageSk);
+ const showAllBox = $$<HTMLInputElement>('.controls checkbox-sk input', changelistsPageSk)!;
expect(showAllBox).to.not.be.null;
- expect(changelistsPageSk._showAll).to.equal(false);
- expectQueryStringToEqual('');
+ expectQueryStringToEqual('?page_size=5');
showAllBox.click();
- expect(changelistsPageSk._showAll).to.equal(true);
- expectQueryStringToEqual('?page_size=50&show_all=true');
+ expectQueryStringToEqual('?page_size=5&show_all=true');
showAllBox.click();
- expect(changelistsPageSk._showAll).to.equal(false);
- expectQueryStringToEqual('?page_size=50');
+ expectQueryStringToEqual('?page_size=5');
showAllBox.click();
- expect(changelistsPageSk._showAll).to.equal(true);
- expectQueryStringToEqual('?page_size=50&show_all=true');
+ expectQueryStringToEqual('?page_size=5&show_all=true');
});
}); // end describe('dynamic content')
});
-function goToNextPage(changelistsPageSk) {
+function goToNextPage(changelistsPageSk: ChangelistsPageSk) {
const event = eventPromise('end-task');
- $$('pagination-sk button.next', changelistsPageSk).click();
+ $$<HTMLButtonElement>('pagination-sk button.next', changelistsPageSk)!.click();
return event;
}
@@ -210,14 +199,17 @@
return event;
}
-function expectFirstPage(changelistsPageSk) {
- expect($('td.owner', changelistsPageSk)[0].innerText).to.contain('alpha');
+function expectFirstPage(changelistsPageSk: ChangelistsPageSk) {
+ expect($<HTMLTableDataCellElement>('td.owner', changelistsPageSk)[0].innerText)
+ .to.contain('alpha');
}
-function expectSecondPage(changelistsPageSk) {
- expect($('td.owner', changelistsPageSk)[0].innerText).to.contain('zeta');
+function expectSecondPage(changelistsPageSk: ChangelistsPageSk) {
+ expect($<HTMLTableDataCellElement>('td.owner', changelistsPageSk)[0].innerText)
+ .to.contain('zeta');
}
-function expectThirdPage(changelistsPageSk) {
- expect($('td.owner', changelistsPageSk)[0].innerText).to.contain('lambda');
+function expectThirdPage(changelistsPageSk: ChangelistsPageSk) {
+ expect($<HTMLTableDataCellElement>('td.owner', changelistsPageSk)[0].innerText)
+ .to.contain('lambda');
}
diff --git a/golden/modules/changelists-page-sk/test_data.js b/golden/modules/changelists-page-sk/test_data.ts
similarity index 83%
rename from golden/modules/changelists-page-sk/test_data.js
rename to golden/modules/changelists-page-sk/test_data.ts
index 5fd8263..533cf28 100644
--- a/golden/modules/changelists-page-sk/test_data.js
+++ b/golden/modules/changelists-page-sk/test_data.ts
@@ -1,7 +1,9 @@
+import { ChangelistsResponse } from '../rpc_types';
+
export const fakeNow = Date.parse('2019-09-09T19:32:30Z');
-export const changelistSummaries_5 = {
- data: [
+export const changelistSummaries_5: ChangelistsResponse = {
+ changelists: [
{
system: 'gerrit',
id: '1788313',
@@ -25,7 +27,10 @@
id: '1788259',
owner: 'gamma@example.org',
status: 'Open',
- subject: "Implement deep content compliance and malware scans for uploads. This is a really long subject, like wow! I hope the web UI doesn't mishandle this massively long subject",
+ subject:
+ "Implement deep content compliance and malware scans for uploads. " +
+ "This is a really long subject, like wow! " +
+ "I hope the web UI doesn't mishandle this massively long subject",
updated: '2019-09-09T19:28:54Z',
url: 'https://chromium-review.googlesource.com/1788259',
},
@@ -48,16 +53,13 @@
url: 'https://chromium-review.googlesource.com/1790066',
},
],
- status: 200,
- pagination: {
- offset: 0,
- size: 5,
- total: 2147483647,
- },
+ offset: 0,
+ size: 5,
+ total: 2147483647,
};
export const changelistSummaries_5_offset5 = {
- data: [
+ changelists: [
{
system: 'gerrit',
id: '1806853',
@@ -104,16 +106,13 @@
url: 'https://chromium-review.googlesource.com/1804507',
},
],
- status: 200,
- pagination: {
- offset: 5,
- size: 5,
- total: 2147483647,
- },
+ offset: 5,
+ size: 5,
+ total: 2147483647,
};
-export const changelistSummaries_5_offset10 = {
- data: [
+export const changelistSummaries_5_offset10: ChangelistsResponse = {
+ changelists: [
{
system: 'gerrit',
id: '1793168',
@@ -160,19 +159,14 @@
url: 'https://chromium-review.googlesource.com/1805646',
},
],
- status: 200,
- pagination: {
- offset: 10,
- size: 5,
- total: 2147483647,
- },
+ offset: 10,
+ size: 5,
+ total: 2147483647,
};
-export const empty = {
- status: 200,
- pagination: {
- offset: 100,
- size: 10,
- total: 100,
- },
-};
+export const empty = (partial?: Partial<ChangelistsResponse>): ChangelistsResponse => ({
+ changelists: null,
+ offset: partial?.offset || 0,
+ size: partial?.size || 5,
+ total: partial?.total || 2147483647,
+});
diff --git a/golden/modules/rpc_types.ts b/golden/modules/rpc_types.ts
index 4aa049d..2727cc5 100644
--- a/golden/modules/rpc_types.ts
+++ b/golden/modules/rpc_types.ts
@@ -170,6 +170,13 @@
total: number;
}
+export interface ChangelistsResponse {
+ changelists: Changelist[] | null;
+ offset: number;
+ size: number;
+ total: number;
+}
+
export type ParamSet = { [key: string]: string[] };
export type ParamSetResponse = { [key: string]: string[] | null };