def test_render_tab_outputs(self): def render_arrow_v1(table, params, *, tab_outputs, **kwargs): self.assertEqual(params["tab"], "tab-x") self.assertEqual(tab_outputs["tab-x"].tab_name, "Tab X") assert_arrow_table_equals( tab_outputs["tab-x"].table, make_table( make_column("X", [1], format="{:,d}"), make_column("Y", ["y"]), ), ) return ArrowRenderResult(make_table()) param_schema = ParamSchema.Dict({"tab": ParamSchema.Tab()}) with ModuleTestEnv(param_schema=param_schema, render_arrow_v1=render_arrow_v1) as env: with arrow_table_context( make_column("X", [1], format="{:,d}"), make_column("Y", ["y"]), dir=env.basedir, ) as (path, _): env.call_render( make_table(), params={"tab": "tab-x"}, tab_outputs={ "tab-x": TabOutput(tab_name="Tab X", table_filename=path.name) }, )
def test_render_using_tab_output(self): def render(table, params): self.assertEqual(params["tabparam"].name, "Tab 1") self.assertEqual( params["tabparam"].columns, { "X": ptypes.RenderColumn("X", "number", "{:,d}"), "Y": ptypes.RenderColumn("Y", "text", None), }, ) assert_frame_equal(params["tabparam"].dataframe, pd.DataFrame({ "X": [1], "Y": ["y"] })) param_schema = ParamSchema.Dict({"tabparam": ParamSchema.Tab()}) with ModuleTestEnv(param_schema=param_schema, render=render) as env: with arrow_table_context( make_column("X", [1], format="{:,d}"), make_column("Y", ["y"]), dir=env.basedir, ) as (path, _): env.call_render( make_table(), params={"tabparam": "tab-1"}, tab_outputs={ "tab-1": TabOutput(tab_name="Tab 1", table_filename=path.name) }, )
def test_clean_tab_unreachable(self): tab = Tab("tab-error", "Buggy Tab") with self.assertRaises(TabOutputUnreachableError): self._call_clean_value( ParamSchema.Tab(), "tab-error", tab_results={tab: StepResult(Path("tab-error.arrow"), [])}, )
def _(self, schema: ParamSchema.Multitab, value: List[str]) -> List[str]: slugs = frozenset( # recurse -- the same way we clean a list. slug for slug in self.clean_value_list( ParamSchema.List(inner_schema=ParamSchema.Tab()), value) if slug is not None) # Order based on `self.tabs`. return [slug for slug in self.tabs.keys() if slug in slugs]
def test_clean_tab_omit_unused_tabs_from_tab_outputs(self): result = self._call_prep_params( ParamSchema.Dict({"x": ParamSchema.Tab()}), {"x": "tab-1"}, tab_results={ Tab("tab-1", "Tab 1"): StepResult(Path("tab-1.arrow"), [TEXT("A")]), Tab("tab-2", "Tab 2"): StepResult(Path("tab-2.arrow"), [TEXT("A")]), Tab("tab-3", "Tab 3"): StepResult(Path("tab-3.arrow"), [TEXT("A")]), }, ) self.assertEqual(result.tab_outputs, {"tab-1": TabOutput("Tab 1", "tab-1.arrow")})
def test_clean_tab_happy_path(self): result = self._call_prep_params( ParamSchema.Dict({"x": ParamSchema.Tab()}), {"x": "tab-1"}, tab_results={ Tab("tab-1", "Tab 1"): StepResult(Path("tab-1.arrow"), [TEXT("A")]) }, ) self.assertEqual( result, PrepParamsResult( {"x": "tab-1"}, tab_outputs={"tab-1": TabOutput("Tab 1", "tab-1.arrow")}, uploaded_files={}, ), )
def test_clean_multicolumn_from_other_tab(self): schema = ParamSchema.Dict({ "tab": ParamSchema.Tab(), "columns": ParamSchema.Multicolumn(tab_parameter="tab"), }) params = {"tab": "tab-2", "columns": ["A-from-tab-1", "A-from-tab-2"]} result = self._call_prep_params( schema, params, input_table_columns=[NUMBER("A-from-tab-1")], tab_results={ Tab("tab-2", "Tab 2"): StepResult(Path("tab-2.arrow"), [NUMBER("A-from-tab-2")]) }, ) self.assertEqual(result.params["columns"], ["A-from-tab-2"])
def test_clean_multicolumn_from_other_tab_that_does_not_exist(self): # The other tab would not exist if the user selected and then deleted # it. result = self._call_prep_params( schema=ParamSchema.Dict({ "tab": ParamSchema.Tab(), "columns": ParamSchema.Multicolumn(tab_parameter="tab"), }), params={ "tab": "tab-missing", "columns": ["A-from-tab-1"] }, input_table_columns=[NUMBER("A-from-tab-1")], tab_results={}, ) # result.params['tab'] is not what we're testing here self.assertEqual(result.params["columns"], [])
def test_param_schema_includes_empty_tuples(): # Bug on 2021-04-21: empty NamedTuple ParamSchema classes evaluate to # False; but they should still be included in the param_schema. spec = load_spec( dict( id_name="x", name="x", category="Clean", parameters=[ dict(id_name="timezone", name="timezone", type="timezone"), dict(id_name="tab", name="tab", type="tab"), dict(id_name="condition", type="condition"), ], ) ) assert spec.param_schema == ParamSchema.Dict( { "timezone": ParamSchema.Timezone(), "tab": ParamSchema.Tab(), "condition": ParamSchema.Condition(), } )
def test_clean_tab_unsupported(self): with self.assertRaisesRegex(RuntimeError, "Unsupported: fetch tab"): clean_value(ParamSchema.Tab(), "", None)
def test_validate_not_string(self): with pytest.raises(ValueError, match="not a string"): S.Tab().validate(3)
def test_validate_ok(self): S.Tab().validate("tab-1")
def test_default(self): # TODO consider changing this to None. [2021-04-20, adamhooper] I think # most/all modules would be compatible. assert S.Tab().default == ""
def test_clean_tab_cycle(self): tab = Tab("tab-1", "Tab 1") with self.assertRaises(TabCycleError): self._call_clean_value(ParamSchema.Tab(), "tab-1", tab_results={tab: None})
def test_clean_tab_missing_tab_selected_gives_none(self): # If the user has selected a nonexistent tab, pretend tab is blank. # # JS sees nonexistent tab slugs. render() doesn't. self.assertIsNone(self._call_clean_value(ParamSchema.Tab(), "tab-XXX"))
def test_clean_tab_no_tab_selected_gives_none(self): self.assertIsNone(self._call_clean_value(ParamSchema.Tab(), ""))