Skip to content

Commit

Permalink
Merge pull request #398 from stasm/report-errors-from-functions
Browse files Browse the repository at this point in the history
Report errors from Functions and Intl objects
  • Loading branch information
stasm authored Jul 23, 2019
2 parents 413a363 + 33f6cb2 commit c40eb5e
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 61 deletions.
12 changes: 8 additions & 4 deletions fluent/src/builtins.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,25 @@ function values(opts) {
export
function NUMBER([arg], opts) {
if (arg instanceof FluentNone) {
return arg;
return new FluentNone(`NUMBER(${arg.valueOf()})`);
}

if (arg instanceof FluentNumber) {
return new FluentNumber(arg.valueOf(), merge(arg.opts, opts));
}
return new FluentNone("NUMBER()");

throw new TypeError("Invalid argument type to NUMBER");
}

export
function DATETIME([arg], opts) {
if (arg instanceof FluentNone) {
return arg;
return new FluentNone(`DATETIME(${arg.valueOf()})`);
}

if (arg instanceof FluentDateTime) {
return new FluentDateTime(arg.valueOf(), merge(arg.opts, opts));
}
return new FluentNone("DATETIME()");

throw new TypeError("Invalid argument type to DATETIME");
}
8 changes: 4 additions & 4 deletions fluent/src/resolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ function resolveExpression(scope, expr) {
function VariableReference(scope, {name}) {
if (!scope.args || !scope.args.hasOwnProperty(name)) {
if (scope.insideTermReference === false) {
scope.reportError(new ReferenceError(`Unknown variable: ${name}`));
scope.reportError(new ReferenceError(`Unknown variable: $${name}`));
}
return new FluentNone(`$${name}`);
}
Expand All @@ -151,7 +151,7 @@ function VariableReference(scope, {name}) {
}
default:
scope.reportError(
new TypeError(`Unsupported variable type: ${name}, ${typeof arg}`)
new TypeError(`Variable type not supported: $${name}, ${typeof arg}`)
);
return new FluentNone(`$${name}`);
}
Expand Down Expand Up @@ -224,8 +224,8 @@ function FunctionReference(scope, {name, args}) {

try {
return func(...getArguments(scope, args));
} catch (e) {
// XXX Report errors.
} catch (err) {
scope.reportError(err);
return new FluentNone(`${name}()`);
}
}
Expand Down
14 changes: 7 additions & 7 deletions fluent/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ export class FluentType {
}

export class FluentNone extends FluentType {
valueOf() {
return null;
constructor(value = "???") {
super(value);
}

toString() {
return `{${this.value || "???"}}`;
return `{${this.value}}`;
}
}

Expand All @@ -63,8 +63,8 @@ export class FluentNumber extends FluentType {
try {
const nf = scope.memoizeIntlObject(Intl.NumberFormat, this.opts);
return nf.format(this.value);
} catch (e) {
// XXX Report the error.
} catch (err) {
scope.reportError(err);
return this.value;
}
}
Expand All @@ -79,8 +79,8 @@ export class FluentDateTime extends FluentType {
try {
const dtf = scope.memoizeIntlObject(Intl.DateTimeFormat, this.opts);
return dtf.format(this.value);
} catch (e) {
// XXX Report the error.
} catch (err) {
scope.reportError(err);
return this.value;
}
}
Expand Down
146 changes: 100 additions & 46 deletions fluent/test/functions_builtin_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,26 @@ suite('Built-in functions', function() {
test('missing argument', function() {
let msg;

errors = [];
msg = bundle.getMessage('num-decimal');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{NUMBER($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof ReferenceError);
assert.ok(errors[0] instanceof ReferenceError);
assert.strictEqual(errors[0].message, "Unknown variable: $arg");

errors = [];
msg = bundle.getMessage('num-percent');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{NUMBER($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof ReferenceError);
assert.ok(errors[0] instanceof ReferenceError);
assert.strictEqual(errors[0].message, "Unknown variable: $arg");

errors = [];
msg = bundle.getMessage('num-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{NUMBER($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof ReferenceError);
assert.ok(errors[0] instanceof ReferenceError);
assert.strictEqual(errors[0].message, "Unknown variable: $arg");
});

test('number argument', function() {
Expand All @@ -55,66 +61,87 @@ suite('Built-in functions', function() {

msg = bundle.getMessage('num-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '1');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof RangeError); // Invalid option value
});

// XXX Functions should report errors.
// https://github.com/projectfluent/fluent.js/issues/106
test('string argument', function() {
const args = {arg: "Foo"};
let msg;

errors = [];
msg = bundle.getMessage('num-decimal');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to NUMBER");

errors = [];
msg = bundle.getMessage('num-percent');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to NUMBER");

errors = [];
msg = bundle.getMessage('num-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to NUMBER");
});

// XXX Functions should report errors.
// https://github.com/projectfluent/fluent.js/issues/106
test('date argument', function() {
const date = new Date('2016-09-29');
const args = {arg: date};
let msg;

errors = [];
msg = bundle.getMessage('num-decimal');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to NUMBER");

errors = [];
msg = bundle.getMessage('num-percent');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to NUMBER");

errors = [];
msg = bundle.getMessage('num-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to NUMBER");
});

test('invalid argument', function() {
const args = {arg: []};
let msg;

errors = [];
msg = bundle.getMessage('num-decimal');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof TypeError);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Variable type not supported: $arg, object");

errors = [];
msg = bundle.getMessage('num-percent');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof TypeError);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Variable type not supported: $arg, object");

errors = [];
msg = bundle.getMessage('num-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{NUMBER($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof TypeError);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Variable type not supported: $arg, object");
});
});

Expand All @@ -131,20 +158,26 @@ suite('Built-in functions', function() {
test('missing argument', function() {
let msg;

errors = [];
msg = bundle.getMessage('dt-default');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{DATETIME($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof ReferenceError);
assert.ok(errors[0] instanceof ReferenceError);
assert.strictEqual(errors[0].message, "Unknown variable: $arg");

errors = [];
msg = bundle.getMessage('dt-month');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{DATETIME($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof ReferenceError);
assert.ok(errors[0] instanceof ReferenceError);
assert.strictEqual(errors[0].message, "Unknown variable: $arg");

errors = [];
msg = bundle.getMessage('dt-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, {}, errors), '{DATETIME($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof ReferenceError);
assert.ok(errors[0] instanceof ReferenceError);
assert.strictEqual(errors[0].message, "Unknown variable: $arg");
});

test('Date argument', function () {
Expand Down Expand Up @@ -172,65 +205,86 @@ suite('Built-in functions', function() {
// may vary depending on the TZ:
// Thu Sep 29 2016 02:00:00 GMT+0200 (CEST)
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), date.toString());
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof RangeError); // Invalid option value
});

// XXX Functions should report errors.
// https://github.com/projectfluent/fluent.js/issues/106
test('number argument', function() {
let args = {arg: 1};
let msg;

errors = [];
msg = bundle.getMessage('dt-default');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to DATETIME");

errors = [];
msg = bundle.getMessage('dt-month');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to DATETIME");

errors = [];
msg = bundle.getMessage('dt-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to DATETIME");
});

// XXX Functions should report errors.
// https://github.com/projectfluent/fluent.js/issues/106
test('string argument', function() {
let args = {arg: 'Foo'};
let msg;

errors = [];
msg = bundle.getMessage('dt-default');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to DATETIME");

errors = [];
msg = bundle.getMessage('dt-month');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to DATETIME");

errors = [];
msg = bundle.getMessage('dt-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME()}');
assert.strictEqual(errors.length, 0);
assert.strictEqual(errors.length, 1);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Invalid argument type to DATETIME");
});

test('invalid argument', function() {
let args = {arg: []};
let msg;

errors = [];
msg = bundle.getMessage('dt-default');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof TypeError);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Variable type not supported: $arg, object");

errors = [];
msg = bundle.getMessage('dt-month');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof TypeError);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Variable type not supported: $arg, object");

errors = [];
msg = bundle.getMessage('dt-bad-opt');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{$arg}');
assert.strictEqual(bundle.formatPattern(msg.value, args, errors), '{DATETIME($arg)}');
assert.strictEqual(errors.length, 1);
assert.ok(errors.pop() instanceof TypeError);
assert.ok(errors[0] instanceof TypeError);
assert.strictEqual(errors[0].message, "Variable type not supported: $arg, object");
});
});
});

0 comments on commit c40eb5e

Please sign in to comment.