test_embed.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import json
  2. import os
  3. import re
  4. import tempfile
  5. import shutil
  6. import traitlets
  7. from ..widgets import IntSlider, IntText, Text, Widget, jslink, HBox, widget_serialization
  8. from ..embed import embed_data, embed_snippet, embed_minimal_html, dependency_state
  9. try:
  10. from io import StringIO
  11. except ImportError:
  12. from StringIO import StringIO
  13. try:
  14. # Python 3
  15. from html.parser import HTMLParser
  16. except ImportError:
  17. # Python 2
  18. from HTMLParser import HTMLParser
  19. class CaseWidget(Widget):
  20. """Widget to test dependency traversal"""
  21. a = traitlets.Instance(Widget, allow_none=True).tag(sync=True, **widget_serialization)
  22. b = traitlets.Instance(Widget, allow_none=True).tag(sync=True, **widget_serialization)
  23. _model_name = traitlets.Unicode('CaseWidgetModel').tag(sync=True)
  24. other = traitlets.Dict().tag(sync=True, **widget_serialization)
  25. class TestEmbed:
  26. def teardown(self):
  27. for w in tuple(Widget.widgets.values()):
  28. w.close()
  29. def test_embed_data_simple(self):
  30. w = IntText(4)
  31. state = dependency_state(w, drop_defaults=True)
  32. data = embed_data(views=w, drop_defaults=True, state=state)
  33. state = data['manager_state']['state']
  34. views = data['view_specs']
  35. assert len(state) == 3
  36. assert len(views) == 1
  37. model_names = [s['model_name'] for s in state.values()]
  38. assert 'IntTextModel' in model_names
  39. def test_cors(self):
  40. w = IntText(4)
  41. code = embed_snippet(w)
  42. # 1 is from the require
  43. assert len(re.findall(' crossorigin', code)) > 1
  44. f = StringIO()
  45. embed_minimal_html(f, w)
  46. assert len(re.findall(' crossorigin', f.getvalue())) > 1
  47. code = embed_snippet(w, cors=False, requirejs=False)
  48. assert ' crossorigin' not in code
  49. f = StringIO()
  50. embed_minimal_html(f, w, cors=False, requirejs=False)
  51. assert ' crossorigin' not in f.getvalue()
  52. code = embed_snippet(w, cors=False, requirejs=True)
  53. assert len(re.findall(' crossorigin', code)) == 1 # 1 is from the require, which is ok
  54. f = StringIO()
  55. embed_minimal_html(f, w, cors=False, requirejs=True)
  56. assert len(re.findall(' crossorigin', f.getvalue())) == 1 # 1 is from the require, which is ok
  57. def test_escape(self):
  58. w = Text('<script A> <ScRipt> </Script> <!-- --> <b>hi</b>')
  59. code = embed_snippet(w)
  60. assert code.find(r'<script A>') == -1
  61. assert code.find(r'\u003cscript A> \u003cScRipt> \u003c/Script> \u003c!-- --> <b>hi</b>') >= 0
  62. f = StringIO()
  63. embed_minimal_html(f, w)
  64. content = f.getvalue()
  65. assert content.find(r'<script A>') == -1
  66. assert content.find(r'\u003cscript A> \u003cScRipt> \u003c/Script> \u003c!-- --> <b>hi</b>') >= 0
  67. def test_embed_data_two_widgets(self):
  68. w1 = IntText(4)
  69. w2 = IntSlider(min=0, max=100)
  70. jslink((w1, 'value'), (w2, 'value'))
  71. state = dependency_state([w1, w2], drop_defaults=True)
  72. data = embed_data(views=[w1, w2], drop_defaults=True, state=state)
  73. state = data['manager_state']['state']
  74. views = data['view_specs']
  75. assert len(state) == 7
  76. assert len(views) == 2
  77. model_names = [s['model_name'] for s in state.values()]
  78. assert 'IntTextModel' in model_names
  79. assert 'IntSliderModel' in model_names
  80. def test_embed_data_complex(self):
  81. w1 = IntText(4)
  82. w2 = IntSlider(min=0, max=100)
  83. jslink((w1, 'value'), (w2, 'value'))
  84. w3 = CaseWidget()
  85. w3.a = w1
  86. w4 = CaseWidget()
  87. w4.a = w3
  88. w4.other['test'] = w2
  89. # Add a circular reference:
  90. w3.b = w4
  91. # Put it in an HBox
  92. HBox(children=[w4])
  93. state = dependency_state(w3)
  94. assert len(state) == 9
  95. model_names = [s['model_name'] for s in state.values()]
  96. assert 'IntTextModel' in model_names
  97. assert 'IntSliderModel' in model_names
  98. assert 'CaseWidgetModel' in model_names
  99. assert 'LinkModel' in model_names
  100. # Check that HBox is not collected
  101. assert 'HBoxModel' not in model_names
  102. # Check that views make sense:
  103. data = embed_data(views=w3, drop_defaults=True, state=state)
  104. assert state is data['manager_state']['state']
  105. views = data['view_specs']
  106. assert len(views) == 1
  107. def test_snippet(self):
  108. class Parser(HTMLParser):
  109. state = 'initial'
  110. states = []
  111. def handle_starttag(self, tag, attrs):
  112. attrs = dict(attrs)
  113. if tag == 'script' and attrs.get('type', '') == "application/vnd.jupyter.widget-state+json":
  114. self.state = 'widget-state'
  115. self.states.append(self.state)
  116. elif tag == 'script' and attrs.get('type', '') == "application/vnd.jupyter.widget-view+json":
  117. self.state = 'widget-view'
  118. self.states.append(self.state)
  119. def handle_endtag(self, tag):
  120. self.state = 'initial'
  121. def handle_data(self, data):
  122. if self.state == 'widget-state':
  123. manager_state = json.loads(data)['state']
  124. assert len(manager_state) == 3
  125. self.states.append('check-widget-state')
  126. elif self.state == 'widget-view':
  127. view = json.loads(data)
  128. assert isinstance(view, dict)
  129. self.states.append('check-widget-view')
  130. w = IntText(4)
  131. state = dependency_state(w, drop_defaults=True)
  132. snippet = embed_snippet(views=w, drop_defaults=True, state=state)
  133. parser = Parser()
  134. parser.feed(snippet)
  135. print(parser.states)
  136. assert parser.states == ['widget-state', 'check-widget-state', 'widget-view', 'check-widget-view']
  137. def test_minimal_html_filename(self):
  138. w = IntText(4)
  139. tmpd = tempfile.mkdtemp()
  140. try:
  141. output = os.path.join(tmpd, 'test.html')
  142. state = dependency_state(w, drop_defaults=True)
  143. embed_minimal_html(output, views=w, drop_defaults=True, state=state)
  144. # Check that the file is written to the intended destination:
  145. with open(output, 'r') as f:
  146. content = f.read()
  147. assert content.splitlines()[0] == '<!DOCTYPE html>'
  148. finally:
  149. shutil.rmtree(tmpd)
  150. def test_minimal_html_filehandle(self):
  151. w = IntText(4)
  152. output = StringIO()
  153. state = dependency_state(w, drop_defaults=True)
  154. embed_minimal_html(output, views=w, drop_defaults=True, state=state)
  155. content = output.getvalue()
  156. assert content.splitlines()[0] == '<!DOCTYPE html>'