Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
1 | <?php |
||
30 | abstract class AbstractTemplateTest extends AbstractTestCase |
||
31 | { |
||
32 | /** |
||
33 | * @var CKEditorRenderer |
||
34 | */ |
||
35 | protected $renderer; |
||
36 | |||
37 | /** |
||
38 | * @var ContainerInterface|\PHPUnit_Framework_MockObject_MockObject |
||
39 | */ |
||
40 | private $container; |
||
41 | |||
42 | /** |
||
43 | * @var Packages|\PHPUnit_Framework_MockObject_MockObject |
||
44 | */ |
||
45 | private $packages; |
||
46 | |||
47 | /** |
||
48 | * @var RequestStack|\PHPUnit_Framework_MockObject_MockObject |
||
49 | */ |
||
50 | private $requestStack; |
||
51 | |||
52 | /** |
||
53 | * @var RouterInterface|\PHPUnit_Framework_MockObject_MockObject |
||
54 | */ |
||
55 | private $router; |
||
56 | |||
57 | /** |
||
58 | * @var EngineInterface|\PHPUnit_Framework_MockObject_MockObject |
||
59 | */ |
||
60 | private $templating; |
||
61 | |||
62 | /** |
||
63 | * @var Request|\PHPUnit_Framework_MockObject_MockObject |
||
64 | */ |
||
65 | private $request; |
||
66 | |||
67 | /** |
||
68 | * {@inheritdoc} |
||
69 | */ |
||
70 | protected function setUp() |
||
71 | { |
||
72 | $this->requestStack = $this->createMock(RequestStack::class); |
||
73 | $this->request = $this->createMock(Request::class); |
||
74 | $this->requestStack->expects($this->any())->method('getCurrentRequest')->will($this->returnValue($this->request)); |
||
75 | $this->router = $this->createMock(RouterInterface::class); |
||
76 | $this->packages = $this->createMock(Packages::class); |
||
77 | $this->packages |
||
78 | ->expects($this->any()) |
||
79 | ->method('getUrl') |
||
80 | ->will($this->returnArgument(0)); |
||
81 | $this->templating = $this->createMock(EngineInterface::class); |
||
82 | |||
83 | $this->renderer = new CKEditorRenderer(new JsonBuilder(), $this->router, $this->packages, $this->requestStack, $this->templating); |
||
84 | } |
||
85 | |||
86 | public function testDefaultState() |
||
87 | { |
||
88 | $this->assertInstanceOf(CKEditorRendererInterface::class, $this->renderer); |
||
89 | } |
||
90 | |||
91 | public function testRenderWithSimpleWidget() |
||
92 | { |
||
93 | $expected = <<<'EOF' |
||
94 | <textarea ><p>value</p></textarea> |
||
95 | <script type="text/javascript"> |
||
96 | var CKEDITOR_BASEPATH = "base_path"; |
||
97 | </script> |
||
98 | <script type="text/javascript" src="js_path"></script> |
||
99 | <script type="text/javascript"> |
||
100 | if (CKEDITOR.instances["id"]) { |
||
101 | CKEDITOR.instances["id"].destroy(true); |
||
102 | delete CKEDITOR.instances["id"]; |
||
103 | } |
||
104 | |||
105 | CKEDITOR.replace("id", []); |
||
106 | </script> |
||
107 | |||
108 | EOF; |
||
109 | |||
110 | $this->assertTemplate($expected, $this->getContext()); |
||
111 | } |
||
112 | |||
113 | public function testRenderWithFullWidget() |
||
114 | { |
||
115 | $context = [ |
||
116 | 'auto_inline' => false, |
||
117 | 'inline' => true, |
||
118 | 'input_sync' => true, |
||
119 | 'config' => ['foo' => 'bar'], |
||
120 | 'plugins' => [ |
||
121 | 'foo' => ['path' => 'path', 'filename' => 'filename'], |
||
122 | ], |
||
123 | 'styles' => [ |
||
124 | 'default' => [ |
||
125 | ['name' => 'Blue Title', 'element' => 'h2', 'styles' => ['color' => 'Blue']], |
||
126 | ], |
||
127 | ], |
||
128 | 'templates' => [ |
||
129 | 'foo' => [ |
||
130 | 'imagesPath' => 'path', |
||
131 | 'templates' => [ |
||
132 | [ |
||
133 | 'title' => 'My Template', |
||
134 | 'html' => '<h1>Template</h1>', |
||
135 | ], |
||
136 | ], |
||
137 | ], |
||
138 | ], |
||
139 | ]; |
||
140 | |||
141 | $expected = <<<EOF |
||
142 | <textarea ><p>value</p></textarea> |
||
143 | <script type="text/javascript"> |
||
144 | var CKEDITOR_BASEPATH = "base_path"; |
||
145 | </script> |
||
146 | <script type="text/javascript" src="js_path"></script> |
||
147 | <script type="text/javascript"> |
||
148 | if (CKEDITOR.instances["id"]) { |
||
149 | CKEDITOR.instances["id"].destroy(true); |
||
150 | delete CKEDITOR.instances["id"]; |
||
151 | } |
||
152 | |||
153 | CKEDITOR.plugins.addExternal("foo", "path", "filename"); |
||
154 | |||
155 | if (CKEDITOR.stylesSet.get("default") === null) { |
||
156 | CKEDITOR.stylesSet.add("default", [{ |
||
157 | "name": "Blue Title", |
||
158 | "element": "h2", |
||
159 | "styles": { |
||
160 | "color": "Blue" |
||
161 | } |
||
162 | }]); |
||
163 | } |
||
164 | |||
165 | CKEDITOR.addTemplates("foo", { |
||
166 | "imagesPath": "path", |
||
167 | "templates": [{ |
||
168 | "title": "My Template", |
||
169 | "html": "<h1>Template<\/h1>" |
||
170 | }] |
||
171 | }); |
||
172 | |||
173 | CKEDITOR.disableAutoInline = true; |
||
174 | |||
175 | var ivory_ckeditor_id = CKEDITOR.inline("id", {"foo": "bar"}); |
||
176 | |||
177 | ivory_ckeditor_id.on('change', function() { |
||
178 | ivory_ckeditor_id.updateElement(); |
||
179 | }); |
||
180 | </script> |
||
181 | EOF; |
||
182 | |||
183 | $this->assertTemplate($expected, array_merge($this->getContext(), $context)); |
||
184 | } |
||
185 | |||
186 | public function testRenderWithJQuery() |
||
187 | { |
||
188 | $expected = <<<'EOF' |
||
189 | <textarea ><p>value</p></textarea> |
||
190 | <script type="text/javascript"> |
||
191 | var CKEDITOR_BASEPATH = "base_path"; |
||
192 | </script> |
||
193 | <script type="text/javascript" src="js_path"></script> |
||
194 | <script type="text/javascript" src="jquery_path"></script> |
||
195 | <script type="text/javascript"> |
||
196 | $(function () { |
||
197 | if (CKEDITOR.instances["id"]) { |
||
198 | CKEDITOR.instances["id"].destroy(true); |
||
199 | delete CKEDITOR.instances["id"]; |
||
200 | } |
||
201 | |||
202 | CKEDITOR.replace("id", []); |
||
203 | }); |
||
204 | </script> |
||
205 | EOF; |
||
206 | |||
207 | $this->assertTemplate($expected, array_merge($this->getContext(), ['jquery' => true])); |
||
208 | } |
||
209 | |||
210 | public function testRenderWithRequireJs() |
||
211 | { |
||
212 | $expected = <<<'EOF' |
||
213 | <textarea ><p>value</p></textarea> |
||
214 | <script type="text/javascript"> |
||
215 | var CKEDITOR_BASEPATH = "base_path"; |
||
216 | </script> |
||
217 | <script type="text/javascript" src="js_path"></script> |
||
218 | <script type="text/javascript"> |
||
219 | require(['ckeditor'], function() { |
||
220 | if (CKEDITOR.instances["id"]) { |
||
221 | CKEDITOR.instances["id"].destroy(true); |
||
222 | delete CKEDITOR.instances["id"]; |
||
223 | } |
||
224 | |||
225 | CKEDITOR.replace("id", []); |
||
226 | }); |
||
227 | </script> |
||
228 | EOF; |
||
229 | |||
230 | $this->assertTemplate($expected, array_merge($this->getContext(), ['require_js' => true])); |
||
231 | } |
||
232 | |||
233 | public function testRenderWithNotAutoloadedWidget() |
||
234 | { |
||
235 | $expected = <<<'EOF' |
||
236 | <textarea ><p>value</p></textarea> |
||
237 | <script type="text/javascript"> |
||
238 | if (CKEDITOR.instances["id"]) { |
||
239 | CKEDITOR.instances["id"].destroy(true); |
||
240 | delete CKEDITOR.instances["id"]; |
||
241 | } |
||
242 | |||
243 | CKEDITOR.replace("id", []); |
||
244 | </script> |
||
245 | EOF; |
||
246 | |||
247 | $this->assertTemplate($expected, array_merge($this->getContext(), ['autoload' => false])); |
||
248 | } |
||
249 | |||
250 | public function testRenderWithDisableWidget() |
||
251 | { |
||
252 | $this->assertTemplate( |
||
253 | '<textarea ><p>value</p></textarea>', |
||
254 | array_merge($this->getContext(), ['enable' => false]) |
||
255 | ); |
||
256 | } |
||
257 | |||
258 | /** |
||
259 | * @param array $context |
||
260 | * |
||
261 | * @return string |
||
262 | */ |
||
263 | abstract protected function renderTemplate(array $context = []); |
||
264 | |||
265 | /** |
||
266 | * @return array |
||
267 | */ |
||
268 | private function getContext() |
||
269 | { |
||
270 | return [ |
||
271 | 'form' => $this->createMock(FormView::class), |
||
272 | 'id' => 'id', |
||
273 | 'value' => '<p>value</p>', |
||
274 | 'enable' => true, |
||
275 | 'async' => false, |
||
276 | 'autoload' => true, |
||
277 | 'auto_inline' => true, |
||
278 | 'inline' => false, |
||
279 | 'jquery' => false, |
||
280 | 'input_sync' => false, |
||
281 | 'require_js' => false, |
||
282 | 'base_path' => 'base_path', |
||
283 | 'js_path' => 'js_path', |
||
284 | 'jquery_path' => 'jquery_path', |
||
285 | 'filebrowsers' => [], |
||
286 | 'config' => [], |
||
287 | 'plugins' => [], |
||
288 | 'styles' => [], |
||
289 | 'templates' => [], |
||
290 | ]; |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * @param string $expected |
||
295 | * @param array $context |
||
296 | */ |
||
297 | private function assertTemplate($expected, array $context) |
||
298 | { |
||
299 | $this->assertSame($this->normalizeOutput($expected), $this->normalizeOutput($this->renderTemplate($context))); |
||
300 | } |
||
301 | |||
302 | /** |
||
303 | * @param string $output |
||
304 | * |
||
305 | * @return string |
||
306 | */ |
||
307 | private function normalizeOutput($output) |
||
308 | { |
||
309 | $mapping = [ |
||
310 | "\n" => '', |
||
311 | ' ' => '', |
||
312 | '{ ' => '{', |
||
313 | ': ' => ':', |
||
314 | '; ' => ';', |
||
315 | ]; |
||
316 | |||
317 | return str_replace(array_keys($mapping), array_values($mapping), $output); |
||
318 | } |
||
319 | } |
||
320 |