diff --git a/src/userlib/js/bootstrap/candid/candid.js b/src/userlib/js/bootstrap/candid/candid.js index 86b65bc47f..6e722b7528 100644 --- a/src/userlib/js/bootstrap/candid/candid.js +++ b/src/userlib/js/bootstrap/candid/candid.js @@ -18,14 +18,6 @@ function renderMethod(name, idl_func, f) { sig.innerHTML = `${name}: ${idl_func.display()}`; item.appendChild(sig); - const button = document.createElement("button"); - button.className = 'btn'; - if (idl_func.annotations.includes('query')) { - button.innerText = 'Query'; - } else { - button.innerText = 'Call'; - } - const inputs = []; idl_func.argTypes.forEach((arg, i) => { const inputbox = UI.renderInput(arg); @@ -33,8 +25,20 @@ function renderMethod(name, idl_func, f) { inputbox.render(item); }); + const button = document.createElement("button"); + button.className = 'btn'; + if (idl_func.annotations.includes('query')) { + button.innerText = 'Query'; + } else { + button.innerText = 'Call'; + } item.appendChild(button); + const random = document.createElement("button"); + random.className = 'btn'; + random.innerText = 'Lucky'; + item.appendChild(random); + const result = document.createElement("div"); result.className = 'result'; const left = document.createElement("span"); @@ -48,13 +52,7 @@ function renderMethod(name, idl_func, f) { const list = document.getElementById("methods"); list.append(item); - button.addEventListener("click", function() { - const args = inputs.map(arg => arg.parse()); - const isReject = inputs.some(arg => arg.isRejected()); - if (isReject) { - return; - } - + function call(args) { left.className = 'left'; left.innerText = 'Waiting...'; right.innerText = '' @@ -79,7 +77,25 @@ function renderMethod(name, idl_func, f) { })().catch(err => { left.className += ' error'; left.innerText = err.message; - }); + }); + } + + random.addEventListener("click", function() { + const args = inputs.map(arg => arg.parse({ random: true })); + const isReject = inputs.some(arg => arg.isRejected()); + if (isReject) { + return; + } + call(args); + }); + + button.addEventListener("click", function() { + const args = inputs.map(arg => arg.parse()); + const isReject = inputs.some(arg => arg.isRejected()); + if (isReject) { + return; + } + call(args); }); }; diff --git a/src/userlib/js/bootstrap/candid/idl-ui.ts b/src/userlib/js/bootstrap/candid/idl-ui.ts index 6365a72491..e09feb4b0f 100644 --- a/src/userlib/js/bootstrap/candid/idl-ui.ts +++ b/src/userlib/js/bootstrap/candid/idl-ui.ts @@ -79,6 +79,38 @@ class Parse extends IDL.Visitor { } } +class Random extends IDL.Visitor { + public visitNull(t: IDL.NullClass, v: string): null { + return null; + } + public visitBool(t: IDL.BoolClass, v: string): boolean { + return Math.random() < 0.5; + } + public visitText(t: IDL.TextClass, v: string): string { + return Math.random().toString(36).substring(6); + } + public visitInt(t: IDL.IntClass, v: string): BigNumber { + return new BigNumber(this.generateNumber(true)); + } + public visitNat(t: IDL.NatClass, v: string): BigNumber { + return new BigNumber(this.generateNumber(false)); + } + public visitFixedInt(t: IDL.FixedIntClass, v: string): BigNumber { + return new BigNumber(this.generateNumber(true)); + } + public visitFixedNat(t: IDL.FixedNatClass, v: string): BigNumber { + return new BigNumber(this.generateNumber(false)); + } + private generateNumber(signed: boolean): number { + const num = Math.floor(Math.random() * 100); + if (signed && Math.random() < 0.5) { + return -num; + } else { + return num; + } + } +} + export function renderInput(t: IDL.Type): InputBox { return t.accept(new Render(), null); } @@ -87,6 +119,15 @@ function parsePrimitive(t: IDL.Type, d: string) { return t.accept(new Parse(), d); } +function generatePrimitive(t: IDL.Type) { + // TODO: in the future we may want to take a string to specify how random values are generated + return t.accept(new Random(), ''); +} + +export interface ParseConfig { + random?: boolean; +} + class InputBox { public input: HTMLInputElement; public status: HTMLElement; @@ -116,14 +157,20 @@ class InputBox { public isRejected(): boolean { return this.value === undefined; } - public parse(): any { + + public parse(config: ParseConfig = {}): any { if (this.form) { - const value = this.form.parse(); + const value = this.form.parse(config); this.value = value; return value; } try { + if (config.random && this.input.value === '') { + const v = generatePrimitive(this.idl); + this.value = v; + return v; + } const value = parsePrimitive(this.idl, this.input.value); if (!this.idl.covariant(value)) { throw new Error(`${this.input.value} is not of type ${this.idl.display()}`); @@ -163,7 +210,7 @@ abstract class InputForm { public open: HTMLElement = document.createElement('button'); public event: string = 'change'; - public abstract parse(): any; + public abstract parse(config: ParseConfig): any; public abstract generateForm(): any; public renderForm(dom: HTMLElement): void { if (this.form.length === 0) { @@ -214,10 +261,10 @@ class RecordForm extends InputForm { this.generateForm(); this.renderForm(dom); } - public parse(): Record | undefined { + public parse(config: ParseConfig): Record | undefined { const v: Record = {}; this.fields.forEach(([key, _], i) => { - const value = this.form[i].parse(); + const value = this.form[i].parse(config); v[key] = value; }); if (this.form.some(input => input.isRejected())) { @@ -247,10 +294,10 @@ class VariantForm extends InputForm { const variant = renderInput(type); this.form = [variant]; } - public parse(): Record | undefined { + public parse(config: ParseConfig): Record | undefined { const select = this.open as HTMLSelectElement; const selected = select.options[select.selectedIndex].text; - const value = this.form[0].parse(); + const value = this.form[0].parse(config); if (value === undefined) { return undefined; } @@ -277,11 +324,11 @@ class OptionForm extends InputForm { this.form = []; } } - public parse(): [T] | [] | undefined { + public parse(config: ParseConfig): [T] | [] | undefined { if (this.form.length === 0) { return []; } else { - const value = this.form[0].parse(); + const value = this.form[0].parse(config); if (value === undefined) { return undefined; } @@ -321,9 +368,9 @@ class VecForm extends InputForm { this.form.forEach(e => e.render(form)); dom.appendChild(form); } - public parse(): T[] | undefined { + public parse(config: ParseConfig): T[] | undefined { const value = this.form.map(input => { - return input.parse(); + return input.parse(config); }); if (this.form.some(input => input.isRejected())) { return undefined; diff --git a/src/userlib/js/bootstrap/candid/index.html b/src/userlib/js/bootstrap/candid/index.html index d64e773ac6..a7a30d2c67 100644 --- a/src/userlib/js/bootstrap/candid/index.html +++ b/src/userlib/js/bootstrap/candid/index.html @@ -52,8 +52,8 @@ border-radius: .25rem; color: #FFF; font-family: sans-serif; - font-size: 18px; - font-weight: 700; + font-size: 16px; + font-weight: 700; margin: 5px; } .popup-form {