a
    .bZ                     @   s   d dl mZ d dlmZ d dlmZ d dlmZm	Z	m
Z
mZ d dlmZ d dlmZmZmZmZmZmZ d dlmZ d dlmZmZ d d	lmZmZmZmZmZm Z  d d
l!m"Z" de#e$dddZ%G dd dZ&dS )    )wraps)log)FunctionType)IterableListDictCallable)files_extensions)TestInfo
TestReturnTestName
TestMethodTestsResultsPathLike)	PIC_FILES)DEFAULT_SUCCESS_MESSAGESCORES_NDIGITS)as_pathwarncached_propertyis_valid_xmlis_url_relative
url_exists)WebsiteN   Ftitleweightc                    s    fdd}|S )a/  Function generating a decorator, to mark a method as a test.

    Tests must return a float between 0 and 1 and a list of logs (strings).

    If `relative` is True, test may return a result above 1.
    The class CollectTestResults will then divide the score of each
    website by the best score.
    c                    s8   t  td fdd}d|_|_|_|_|S )Nreturnc                    s    | g|R i |}t |trt|dkrt |d  }ttfrdt|t }krs`|dkrt |d  }trtdd |D r|s|dkr|	t
 ||fS td jd|dd S )	N   r   r   c                 s   s   | ]}t |tV  qd S N)
isinstancestr).0elt r&   V/home/manager/.local/lib/python3.9/site-packages/websites_test_framework/test_class.py	<genexpr>2       zEtest.<locals>.mark_function_as_test.<locals>.new_f.<locals>.<genexpr>z'Invalid return values for website test : zb
A test must return (success_rate, log) where 0 <= success_rate <= 1 and log is a list of strings.)r"   tuplelenfloatintroundr   listallappendr   
ValueError__qualname__)selfargskwresultscorer   )frelativer&   r'   new_f'   s.    
	
z2test.<locals>.mark_function_as_test.<locals>.new_fT)r   r   is_testr   r   is_relative)r:   r<   r;   r   r   )r:   r'   mark_function_as_test&   s    z#test.<locals>.mark_function_as_testr&   )r   r   r;   r@   r&   r?   r'   test   s    
rA   c                   @   s$  e Zd ZdZdZe dfeee edddZ	dd Z
ee d	d
dZeee d	ddZeee d	ddZeeedddZeeeef d	ddZdneegef edddZeddddd Zedddd d! Zed"ddd#d$ Zedoe e!d&d'd(Z"ed)dde!d	d*d+Z#ed,dde!d	d-d.Z$ed/d0de!d	d1d2Z%ed3d4de!d	d5d6Z&ed7dd8d9e!d	d:d;Z'ed<dd8d9e!d	d=d>Z(ed?dd8d9d@dA Z)edBdde!d	dCdDZ*edEdde!d	dFdGZ+edHdIde!d	dJdKZ,edLdMde!d	dNdOZ-edPdde!d	dQdRZ.edSdTde!d	dUdVZ/edWdde!d	dXdYZ0edZdde!d	d[d\Z1ed]dde!d	d^d_Z2ed`dde!d	dadbZ3edcdde!d	dddeZ4dfdg Z5edhdde!d	didjZ6edkdde!d	dldmZ7dS )pWebsiteTesta  Base class for testing a website.

    Main method is run(), which return a dictionary with all tests' results.

    To write a battery of tests, subclass this class, and add custom tests as methods.

    Each method decorated with @test is a test.

    Each test must return:
        - the result: a float between 0 and 1,
        - the log: a string.

    One should also overwrite `.get_authors()` method, which must return the lists
    of the authors of the website.
    F/)website_pathskipped_testspath_on_serverc                 C   sB   t || _t| j|| _|| _t| jjdkr>tdt| j d S )Nr   zNo HTML file)	r   pathr   websiterE   r,   
html_filesr   r#   )r5   rD   rE   rF   r&   r&   r'   __init__Y   s
    
zWebsiteTest.__init__c                 C   s   | j j d| j dS )Nz('z'))	__class____name__rG   r5   r&   r&   r'   __str__f   s    zWebsiteTest.__str__r   c                 C   s   t dS )z}WebsiteTest subclasses should implement this method.

        It must return the list of the authors of the website.
        N)NotImplementedErrorrM   r&   r&   r'   get_authorsm   s    zWebsiteTest.get_authorsc                    s    fdd j  D S )zjReturn active tests' names.

        Tests' name included in self.skipped_tests are not returned.
        c                    s   g | ]}| j vr|qS r&   )rE   r$   namerM   r&   r'   
<listcomp>~   s   z+WebsiteTest.tests_names.<locals>.<listcomp>)rK   get_all_tests_namesrM   r&   rM   r'   tests_namesx   s    
zWebsiteTest.tests_namesc                    s   t  fddtD S )a!  Return sorted tests' names list, where name is method's name.

        Tests are sorted by name.

        All tests' names are returned, even those included in self.skipped_tests.
        This is a low-level method, usage of `.tests_names` property is recommended
        instead.
        c                 3   s6   | ].}t t|  trt d drt|V  qdS )r=   FN)r"   getattrr   r   rQ   	attributeclsr&   r'   r(      s   z2WebsiteTest.get_all_tests_names.<locals>.<genexpr>)sorteddirrY   r&   rW   r'   rT      s    
zWebsiteTest.get_all_tests_names)rR   r   c                 C   s
   t | |S r!   )rV   )rY   rR   r&   r&   r'   _test   s    zWebsiteTest._testc                    s    fdd   D S )zXGet title and weight for all tests.

        Tests are sorted by (method) name.
        c                    s2   i | ]*}|  |j  |j  |jd qS ))r   r   r>   )r]   r   r   r>   rQ   r\   r&   r'   
<dictcomp>   s   


z/WebsiteTest.get_tests_infos.<locals>.<dictcomp>)rT   r\   r&   r\   r'   get_tests_infos   s    
zWebsiteTest.get_tests_infosN)filter_funcr   c                 C   sT   t d| j d i }t|| jD ],}t| | }|\}}|d|d||< q"|S )zMain method: Run all tests and return results as a dict.

        Tests' results are sorted by test's name.

        If `filter_func` is not `None`, it should be a function taking a test's name as argument
        and returning a boolean value.
        z
[1mTesting z...[0m
)r9   r   )printrG   filterrU   rV   join)r5   r`   Zall_resultsrR   r8   r9   r   r&   r&   r'   run   s    zWebsiteTest.runzUTF8 ?r   r   c                 C   s~   d}d}g }| j jD ]6}|d7 }|jr0|d7 }q||j d|j d q|dkr^ddgfS d| | ||krtdnd |fS )Nr   r   u    doit être encodé en UTF8 (u    détecté).zNo HTML/CSS files !皙?皙?)rH   Z	web_filesZis_utf8r2   relative_pathencoding)r5   Zfiles_numberr9   r   Ztested_filer&   r&   r'   	test_utf8   s    

zWebsiteTest.test_utf8charset      ?c                 C   s   d}g }t | jjsddgfS | jjD ]z}|jjddd}t |dkr|d7 }|d jd   dd	d
krz|d7 }q|	d|j
  q&|	d|j
  q&|dt | jj  |fS )Nr   No HTML file !metaT)rk   r   rk   - utf8zBad encoding declaration: zNo encoding declaration: r    )r,   rH   rI   	structurefind_allattrslowerstripreplacer2   rh   )r5   r9   r   	html_fileZmetasr&   r&   r'   test_declared_encoding   s    
"
z"WebsiteTest.test_declared_encodingzXML ?c                 C   s   g }d}| j j}t|dkr&ddgfS |D ]:}t|j\}}|rH|d7 }|r*|d|j d|  q*d| t| }|t|kr|d7 }tdd	 |D t| |fS )
Nr   rm   r   File r*   ?皙?c                 s   s   | ]}|j V  qd S r!   )r   )r$   rx   r&   r&   r'   r(      r)   z'WebsiteTest.test_xml.<locals>.<genexpr>)rH   rI   r,   r   rG   r2   rh   sum)r5   r   counterfilesrx   Zvalidmsgr9   r&   r&   r'   test_xml   s    
zWebsiteTest.test_xml{Gz?)target_ratior   c           
      C   s  d|  krdk sn t dg }d}| D ]}||j q*dt| g}t|dkr|dtt|t|  d 7 }tdd | D }|d	tt|||  d 7 }td
d |D }tdd | D }	|d	t|||	  d 7 }d|  krdksn J ||d| |  }||fS )zTest if files are enough commented.

        `target_ratio` must be a float between 0 and 1.
        Maximal score is obtained when comment ratio >= target_ratio.
        r   r   z-`target_ratio` value must be between 0 and 1.        zNombre de commentaires: g?c                 s   s   | ]}|j V  qd S r!   )number_of_linesr$   filer&   r&   r'   r(   
  r)   z-WebsiteTest._test_comments.<locals>.<genexpr>333333?c                 s   s   | ]}t |V  qd S r!   )r,   )r$   commentr&   r&   r'   r(     r)   c                 s   s   | ]}|j V  qd S r!   )Znumber_of_charactersr   r&   r&   r'   r(     r)   )r3   extendcommentsr,   minr}   )
r   minimal_score_if_any_commentr   r   r9   r   r   r   Ztotal_comments_charactersZtotal_charactersr&   r&   r'   _test_comments   s"    	 zWebsiteTest._test_commentszHTML commentsc                 C   s   | j | jjdddS )Nrl   r   r   r   )r   rH   rI   rM   r&   r&   r'   test_html_comments  s    
zWebsiteTest.test_html_commentszCSS commentsc                 C   s   | j | jjdddS )N      ?g{Gz?r   )r   rH   used_css_filesrM   r&   r&   r'   test_commentaires_css  s    
z!WebsiteTest.test_commentaires_csszHTML Validity   c                 C   s   g }d}| j jD ]8}||j ||j |t|jdt|j  7 }qt| j jdkrdddgfS dd| t| j jd| j j    }|dkrddgfS td	||fS )
Nr   r   zNo HTML file found !rl   rf   r   r   zHTML: OKr   )rH   rI   r   errorswarningsr,   tags_global_numbermax)r5   r   penaltyr   r9   r&   r&   r'   test_html_validity"  s    

zWebsiteTest.test_html_validityzCSS Validity   c                 C   s   g }d}| j jD ]4}|j}|dkr<|d|j d| d ||7 }qt| j jdkr`ddgfS dd| t| j jd| j j    }|dkrd	d
gfS td||fS )Nr   rz   r*   z error(s) found.zNo CSS file found !rl   rf   r   r   zCSS: OKr   )rH   	css_filesr   r2   rR   r,   Zcss_rules_global_numberr   )r5   r   r   r   nr9   r&   r&   r'   test_css_validity4  s    


zWebsiteTest.test_css_validityzHTML diversityT)r   r   r;   c                 C   sz   g }| j j}| j j}tdd |D }|d|  |d|  |dkrVd|fS tdtd| | |d }||fS )	Nc                 s   s   | ]}|d krdV  qdS )brr   Nr&   )r$   keyr&   r&   r'   r(   M  r)   z2WebsiteTest.test_html_diversity.<locals>.<genexpr>zTotal tags: zTotal different tags: r         ?
      )rH   
tags_countr   r}   r2   r   r   )r5   r   r   
total_tagsZtotal_different_tagsr9   r&   r&   r'   test_html_diversityG  s    zWebsiteTest.test_html_diversityz
div & spanc                 C   sb   | j j}|d |d  }d| g}t| }|dkrL|dkrDdnd|fS tdd| | |fS )NdivspanzGeneric tags: r   r   r   r|   )rH   r   r}   valuesr   )r5   r   Zdiv_span_countr   r   r&   r&   r'   test_not_to_much_div_and_spanV  s    z)WebsiteTest.test_not_to_much_div_and_spanz	HTML sizec                 C   s4   | j j}t| |d  |d  }|d| gfS )z8Return the total number of tags, excluding div and span.r   r   zTags number: )rH   r   r}   r   )r5   r   r   r&   r&   r'   test_number_of_tags`  s    zWebsiteTest.test_number_of_tagsz@rulesc                 C   s0   t dd | jjD }td|d d| gfS )z8Test for @rules. Score is maximal if @rules number >= 3.c                 s   s   | ]}t |jV  qd S r!   )r,   at_rules_namesr   r&   r&   r'   r(   j  r)   z0WebsiteTest.test_for_at_rules.<locals>.<genexpr>r   r   zNumber of @rules: )r}   rH   r   r   )r5   r   r&   r&   r'   test_for_at_rulesg  s    zWebsiteTest.test_for_at_rules@mediac                 C   s   t dd | jjD g fS )Nc                 s   s   | ]}d |j v V  qdS )r   Nr   r   r&   r&   r'   r(   p  r)   z2WebsiteTest.test_for_media_rule.<locals>.<genexpr>anyrH   r   rM   r&   r&   r'   test_for_media_rulem  s    zWebsiteTest.test_for_media_rule
@font-faceg333333?c                 C   s(   t dd | jjD }||r dndgfS )Nc                 s   s   | ]}d |j v V  qdS )r   Nr   r   r&   r&   r'   r(   v  r)   z6WebsiteTest.test_for_font_face_rule.<locals>.<genexpr>Used.	Not used.r   r5   foundr&   r&   r'   test_for_font_face_rulet  s    z#WebsiteTest.test_for_font_face_rule
@keyframesr   c                 C   s(   t dd | jjD }||r dndgfS )Nc                 s   s   | ]}d |j v V  qdS )r   Nr   r   r&   r&   r'   r(   {  r)   z6WebsiteTest.test_for_keyframes_rule.<locals>.<genexpr>r   r   r   r   r&   r&   r'   test_for_keyframes_ruley  s    z#WebsiteTest.test_for_keyframes_rulezNo JSc                 C   sF   | j jD ](}|jd dkrdd|j gf  S q| j jr>dndg fS )Nscriptr   zJS in r   )rH   rI   r   rR   )r5   rx   r&   r&   r'   test_no_javascript~  s    zWebsiteTest.test_no_javascriptzadvanced selectorsr    c                    s   d}g }dD ]D t  fdd| jjD }|d  d|  |td| 7 }qt dd | jjD }|d	|  |d
krt|td|  dnd
|fS )Nr   z<+~:[c                 3   s"   | ]}|j D ]} |v V  qqd S r!   )	selectors)r$   r   selectorsymbolr&   r'   r(     s   z:WebsiteTest.test_for_advanced_selectors.<locals>.<genexpr>z	Selector z : r   c                 s   s   | ]}t |jV  qd S r!   )r,   r   r   r&   r&   r'   r(     r)   zTotal: r   r   )r}   rH   r   r2   lnr   )r5   r9   r   r   r&   r   r'   test_for_advanced_selectors  s    z'WebsiteTest.test_for_advanced_selectors
index.htmlc                 C   s   | j d  sddgfS d }}g }| j dD ]X}| s@J tdd |dD r0|d  s|d7 }|d	| d
 q0|d7 }q0|rdd| ||   |fS d|fS )Nr   r   zNo index.html file !z**c                 s   s   | ]}|j d kV  qdS )z.htmlN)suffix)r$   pthr&   r&   r'   r(     r)   z-WebsiteTest.test_for_index.<locals>.<genexpr>*r   zNo index.html file in .rg   rf   )rG   is_fileglobis_dirr   r2   r5   goodbadr   rG   r&   r&   r'   test_for_index  s    

zWebsiteTest.test_for_indexz
File typesc                 C   s   d }}g }| j  D ]^}|j }|tjv r8|d7 }q|d7 }|d|d|jd |tjvrt	d|d q|| dkrddgfS |d	| |  |fS )
Nr   r   zBad file type: z ()zUnreferenced file type: z1Update `websites_test_framework.files_extensions`z	No file !r    )
rH   iterate_over_files_pathsr   ru   r	   Z
GOOD_FILESr2   rR   Z	BAD_FILESr   )r5   r   r   r   rG   extr&   r&   r'   test_files_types  s     




zWebsiteTest.test_files_typesz
img folderc                 C   s   d }}g }| j jt D ]@}|jj ddv r<|d7 }q|d7 }|d|jj  q|| dkr|| j jrrdnddgfS |||  |fS )Nr   s)imgZpicZpictureZphotoimager   zParent image directory: rf   zNo image file.)	rH   r   r   parentrR   ru   rstripr2   rI   r   r&   r&   r'   test_img_folder  s    
zWebsiteTest.test_img_folderz
css folderc                 C   sz   d }}g }| j dD ]:}|jj dv r6|d7 }q|d7 }|d|jj  q|| dkrjddgfS |||  |fS )Nr   z.css)cssstylestyles
stylesheetZstylesheetsr   zParent css directory: zNo CSS file !)rH   r   r   rR   ru   r2   r   r&   r&   r'   test_css_folder  s    

zWebsiteTest.test_css_folderz
Unused cssc                 C   s   | j jsddgfS t| j j}t| j j}||kr:dg fS ||k shJ d| j j ddd | j jD  d| | ||  d	gfS )
Nr   zNot any used CSS filesr   zPath: z
Used:c                 S   s   g | ]
}|j qS r&   )rG   r   r&   r&   r'   rS     r)   z/WebsiteTest.test_unused_css.<locals>.<listcomp>rl   z unused CSS files)rH   r   r,   r   root)r5   usedtotalr&   r&   r'   test_unused_css  s    
 zWebsiteTest.test_unused_cssc           
      C   s0  g }d}z|j d  }z|dd\}}W n tyD   d}Y n0 |rbt|rr|jj|   sp|d7 }n|	dr| j
|}| j
j|d   s|d7 }nt|	ds|	dr
t|}|d u rtd	| d
|j d } || n|s |d7 }n|	dr |d7 }n |dkrPtd| d
|j  } || n|d|  n|r|rrJ t|jjj|d}	|	dkrtd|d|j  } |d7 }n8|	dkrd| }n"td|d|j  } |d7 }|| W n. ty&   |d7 }|d| d Y n0 ||fS )Nr   href#r   rp   rC   zhttp:zhttps:z(Can't test z from z-: server down or access forbidden, skipping.)rg   zfile:Broken link: z	Link OK: )idzId z not found : file rl   zFragment OK: #z is not unique: file zInvalid <a> tag: r   )rt   rv   splitr3   r   rG   r   resolveexists
startswithrH   Zrewrite_absolute_urlr   lstripr   rb   r2   r,   rr   htmlrs   KeyError)
r5   r   tagr   r   urlfragmentexistr   r   r&   r&   r'   	_test_url  s^    










zWebsiteTest._test_urlzBroken css linksc           
   	   C   s  d}d}g }| j jD ]}|jjd u rT|d7 }|d7 }td|j  } || q|jjdD ]@}|j	ddgkrb|d7 }| 
||\}}||7 }|| qbq| j jD ]}zj|j D ]Z}	t|	r|d7 }|jj|	   s|d7 }td|	  } || q|	drqW q tyR   |d7 }|d7 }|d	|j  Y q0 q|dkrjdd
gfS td|dkr|dndd||   |fS )Nr   r   Bad HTML file: linkrelr   r   rC   zBad CSS file: zNo CSS file linked.r{   )rH   rI   rr   headrb   rG   r2   rs   rt   getr   r   r   Zlist_importsr   r   r   r   r   LookupErrorr   )
r5   r   r   r   r   r   r   r   log_r   r&   r&   r'   test_broken_css_references  sB    


z&WebsiteTest.test_broken_css_referenceszBroken urlsc           	      C   s   d}d}g }| j jD ]~}|jjd u rT|d7 }|d7 }td|j  } || q|jjdD ].}|d7 }| ||\}}||7 }|	| qbq|dkrddgfS t
d|dkrdndd||   |fS )Nr   r   r   azNo link found.gffffff?)rH   rI   rr   r   rb   rG   r2   rs   r   r   r   )	r5   r   r   r   r   r   r   r   r   r&   r&   r'   test_broken_url_in_htmlB  s"    
z#WebsiteTest.test_broken_url_in_html)N)rl   r   )8rL   
__module__r4   __doc__Z__test__r+   r   r   r#   rJ   rN   r   rP   r   r   rU   classmethodrT   r   r]   r   r
   r_   r   boolr   re   rA   rj   ry   r   staticmethodr-   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r&   r&   r&   r'   rB   F   s   	





 !



	











9
$
rB   )Nr   F)'	functoolsr   mathr   r   typesr   typingr   r   r   r   Zwebsites_test_frameworkr	   Z$websites_test_framework.custom_typesr
   r   r   r   r   r   Z(websites_test_framework.files_extensionsr   Zwebsites_test_framework.paramr   r   Zwebsites_test_framework.toolsr   r   r   r   r   r   Zwebsites_test_framework.websiter   r#   r-   rA   rB   r&   r&   r&   r'   <module>   s     *