prosody/spec/util_dataforms_spec.lua
Kim Alvefur 8959868a28 util.stanza: Add method for extracting a single attribute value
Sometimes you only care about a single attribute, but the child tag
itself may be optional, leading to needing `tag and tag.attr.foo` or
`stanza:find("tag@foo")`.

The `:find()` method is fairly complex, so avoiding it for this kind of
simpler use case is a win.
2022-08-17 19:04:30 +02:00

512 lines
14 KiB
Lua

local dataforms = require "util.dataforms";
local st = require "util.stanza";
local jid = require "util.jid";
local iter = require "util.iterators";
describe("util.dataforms", function ()
local some_form, xform;
setup(function ()
some_form = dataforms.new({
title = "form-title",
instructions = "form-instructions",
{
type = "hidden",
name = "FORM_TYPE",
value = "xmpp:prosody.im/spec/util.dataforms#1",
};
{
type = "fixed";
value = "Fixed field";
},
{
type = "boolean",
label = "boolean-label",
name = "boolean-field",
value = true,
},
{
type = "fixed",
label = "fixed-label",
name = "fixed-field",
value = "fixed-value",
},
{
type = "hidden",
label = "hidden-label",
name = "hidden-field",
value = "hidden-value",
},
{
type = "jid-multi",
label = "jid-multi-label",
name = "jid-multi-field",
value = {
"jid@multi/value#1",
"jid@multi/value#2",
},
},
{
type = "jid-single",
label = "jid-single-label",
name = "jid-single-field",
value = "jid@single/value",
},
{
type = "list-multi",
label = "list-multi-label",
name = "list-multi-field",
value = {
"list-multi-option-value#1",
"list-multi-option-value#3",
},
options = {
{
label = "list-multi-option-label#1",
value = "list-multi-option-value#1",
default = true,
},
{
label = "list-multi-option-label#2",
value = "list-multi-option-value#2",
default = false,
},
{
label = "list-multi-option-label#3",
value = "list-multi-option-value#3",
default = true,
},
}
},
{
type = "list-single",
label = "list-single-label",
name = "list-single-field",
value = "list-single-value",
options = {
"list-single-value",
"list-single-value#2",
"list-single-value#3",
}
},
{
type = "text-multi",
label = "text-multi-label",
name = "text-multi-field",
value = "text\nmulti\nvalue",
},
{
type = "text-private",
label = "text-private-label",
name = "text-private-field",
value = "text-private-value",
},
{
type = "text-single",
label = "text-single-label",
name = "text-single-field",
value = "text-single-value",
},
{
-- XEP-0221
-- TODO Validate the XML produced by this.
type = "text-single",
label = "text-single-with-media-label",
name = "text-single-with-media-field",
media = {
height = 24,
width = 32,
{
type = "image/png",
uri = "data:",
},
},
},
});
xform = some_form:form();
end);
it("XML serialization looks like it should", function ()
assert.truthy(xform);
assert.truthy(st.is_stanza(xform));
assert.equal("x", xform.name);
assert.equal("jabber:x:data", xform.attr.xmlns);
assert.equal("FORM_TYPE", xform:get_child_attr("field", nil, "var"));
assert.equal("xmpp:prosody.im/spec/util.dataforms#1", xform:find("field/value#"));
local allowed_direct_children = {
title = true,
instructions = true,
field = true,
}
for tag in xform:childtags() do
assert.truthy(allowed_direct_children[tag.name], "unknown direct child");
end
end);
it("produced boolean field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "boolean-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("boolean-field", f.attr.var);
assert.equal("boolean", f.attr.type);
assert.equal("boolean-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
local val = f:get_child_text("value");
assert.truthy(val == "true" or val == "1");
end);
it("produced fixed field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "fixed-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("fixed-field", f.attr.var);
assert.equal("fixed", f.attr.type);
assert.equal("fixed-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
assert.equal("fixed-value", f:get_child_text("value"));
end);
it("produced hidden field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "hidden-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("hidden-field", f.attr.var);
assert.equal("hidden", f.attr.type);
assert.equal("hidden-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
assert.equal("hidden-value", f:get_child_text("value"));
end);
it("produced jid-multi field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "jid-multi-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("jid-multi-field", f.attr.var);
assert.equal("jid-multi", f.attr.type);
assert.equal("jid-multi-label", f.attr.label);
assert.equal(2, iter.count(f:childtags("value")));
local i = 0;
for value in f:childtags("value") do
i = i + 1;
assert.equal(("jid@multi/value#%d"):format(i), value:get_text());
end
end);
it("produced jid-single field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "jid-single-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("jid-single-field", f.attr.var);
assert.equal("jid-single", f.attr.type);
assert.equal("jid-single-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
assert.equal("jid@single/value", f:get_child_text("value"));
assert.truthy(jid.prep(f:get_child_text("value")));
end);
it("produced list-multi field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "list-multi-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("list-multi-field", f.attr.var);
assert.equal("list-multi", f.attr.type);
assert.equal("list-multi-label", f.attr.label);
assert.equal(2, iter.count(f:childtags("value")));
assert.equal("list-multi-option-value#1", f:get_child_text("value"));
assert.equal(3, iter.count(f:childtags("option")));
end);
it("produced list-single field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "list-single-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("list-single-field", f.attr.var);
assert.equal("list-single", f.attr.type);
assert.equal("list-single-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
assert.equal("list-single-value", f:get_child_text("value"));
assert.equal(3, iter.count(f:childtags("option")));
end);
it("produced text-multi field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "text-multi-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("text-multi-field", f.attr.var);
assert.equal("text-multi", f.attr.type);
assert.equal("text-multi-label", f.attr.label);
assert.equal(3, iter.count(f:childtags("value")));
end);
it("produced text-private field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "text-private-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("text-private-field", f.attr.var);
assert.equal("text-private", f.attr.type);
assert.equal("text-private-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
assert.equal("text-private-value", f:get_child_text("value"));
end);
it("produced text-single field correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "text-single-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("text-single-field", f.attr.var);
assert.equal("text-single", f.attr.type);
assert.equal("text-single-label", f.attr.label);
assert.equal(1, iter.count(f:childtags("value")));
assert.equal("text-single-value", f:get_child_text("value"));
end);
describe("get_type()", function ()
it("identifes dataforms", function ()
assert.equal(nil, dataforms.get_type(nil));
assert.equal(nil, dataforms.get_type(""));
assert.equal(nil, dataforms.get_type({}));
assert.equal(nil, dataforms.get_type(st.stanza("no-a-form")));
assert.equal("xmpp:prosody.im/spec/util.dataforms#1", dataforms.get_type(xform));
end);
end);
describe(":data", function ()
it("returns something", function ()
assert.truthy(some_form:data(xform));
end);
end);
describe("issue1177", function ()
local form_with_stuff;
setup(function ()
form_with_stuff = dataforms.new({
{
type = "list-single";
name = "abtest";
label = "A or B?";
options = {
{ label = "A", value = "a", default = true },
{ label = "B", value = "b" },
};
},
});
end);
it("includes options when value is included", function ()
local f = form_with_stuff:form({ abtest = "a" });
assert.truthy(f:find("field/option"));
end);
it("includes options when value is excluded", function ()
local f = form_with_stuff:form({});
assert.truthy(f:find("field/option"));
end);
end);
describe("using current values in place of missing fields", function ()
it("gets back the previous values when given an empty form", function ()
local current = {
["list-multi-field"] = {
"list-multi-option-value#2";
};
["list-single-field"] = "list-single-value#2";
["hidden-field"] = "hidden-value";
["boolean-field"] = false;
["text-multi-field"] = "words\ngo\nhere";
["jid-single-field"] = "alice@example.com";
["text-private-field"] = "hunter2";
["text-single-field"] = "text-single-value";
["jid-multi-field"] = {
"bob@example.net";
};
};
local expect = {
-- FORM_TYPE = "xmpp:prosody.im/spec/util.dataforms#1"; -- does this need to be included?
["list-multi-field"] = {
"list-multi-option-value#2";
};
["list-single-field"] = "list-single-value#2";
["hidden-field"] = "hidden-value";
["boolean-field"] = false;
["text-multi-field"] = "words\ngo\nhere";
["jid-single-field"] = "alice@example.com";
["text-private-field"] = "hunter2";
["text-single-field"] = "text-single-value";
["jid-multi-field"] = {
"bob@example.net";
};
};
local data, err = some_form:data(st.stanza("x", {xmlns="jabber:x:data"}), current);
assert.is.table(data, err);
assert.same(expect, data, "got back the same data");
end);
end);
describe("field 'var' property", function ()
it("works as expected", function ()
local f = dataforms.new {
{
var = "someprefix#the-field",
name = "the_field",
type = "text-single",
}
};
local x = f:form({the_field = "hello"});
assert.equal("someprefix#the-field", x:find"field@var");
assert.equal("hello", x:find"field/value#");
end);
end);
describe("number handling", function()
it("handles numbers as booleans", function()
local f = dataforms.new { { name = "boolean"; type = "boolean" } };
local x = f:form({ boolean = 0 });
assert.equal("0", x:find "field/value#");
x = f:form({ boolean = 1 });
assert.equal("1", x:find "field/value#");
end);
end)
describe("datatype validation", function ()
describe("integer", function ()
local f = dataforms.new {
{
name = "number",
type = "text-single",
datatype = "xs:integer",
range_min = -10,
range_max = 10,
},
};
it("roundtrip works", function ()
local d = f:data(f:form({number = 1}));
assert.equal(1, d.number);
end);
it("error handling works", function ()
local d,e = f:data(f:form({number = "nan"}));
assert.not_equal(1, d.number);
assert.table(e);
assert.string(e.number);
end);
it("bounds-checking work works", function ()
local d,e = f:data(f:form({number = 100}));
assert.not_equal(100, d.number);
assert.table(e);
assert.string(e.number);
end);
it("serializes larger ints okay", function ()
local x = f:form{number=1125899906842624}
assert.equal("1125899906842624", x:find("field/value#"))
end);
end)
describe("datetime", function ()
local f = dataforms.new { { name = "when"; type = "text-single"; datatype = "xs:dateTime" } }
it("works", function ()
local x = f:form({ when = 1219439340 });
assert.equal("2008-08-22T21:09:00Z", x:find("field/value#"))
local d, e = f:data(x);
assert.is_nil(e);
assert.same({ when = 1219439340 }, d);
end);
end)
end);
describe("media element", function ()
it("produced media element correctly", function ()
local f;
for field in xform:childtags("field") do
if field.attr.var == "text-single-with-media-field" then
f = field;
break;
end
end
assert.truthy(st.is_stanza(f));
assert.equal("text-single-with-media-field", f.attr.var);
assert.equal("text-single", f.attr.type);
assert.equal("text-single-with-media-label", f.attr.label);
assert.equal(0, iter.count(f:childtags("value")));
local m = f:get_child("media", "urn:xmpp:media-element");
assert.truthy(st.is_stanza(m));
assert.equal("24", m.attr.height);
assert.equal("32", m.attr.width);
assert.equal(1, iter.count(m:childtags("uri")));
local u = m:get_child("uri");
assert.truthy(st.is_stanza(u));
assert.equal("image/png", u.attr.type);
assert.equal("data:", u:get_text());
end);
end);
end);